pk.org: Computer Security/Lecture Notes

Malware

Part 3 - Delivery and Persistence

Paul Krzyzanowski – 2026-11-03

A chain of scripts and downloads ran on the Lumma victim’s machine before the credential stealer started. An infostealer may then complete its work in a single run, but an attacker who wants remote control needs the installed code to resume after every reboot. A startup service can provide that repeated execution.

Malware can also change the software that administrators use to search for and inspect it. An altered file-listing utility can omit a filename. A kernel driver can conceal the same file at the system-call level, hiding it from many utilities at once, so a defender inspecting that machine may be relying on software the attacker has already changed.

We continue the malware lifecycle with delivery: the components that carry a payload from its first execution to its final form. Persistence follows: how malware registers itself to run again, from startup items and services down to boot code and firmware, and how hardware verifies a machine’s startup. Concealment with rootkits and mobile malware follows. Part 4 covers triggers, operator communication, payloads, propagation, and detection.

Delivery: Droppers, Loaders, and Stages

A malicious program must reach execution and accomplish its objective. Attackers can do this in a single file or spread it across several programs, scripts, and services.

Why Delivery Uses Stages

In an April 2025 Lumma campaign targeting organizations in Canada, a fake verification page instructed visitors to paste and run a command. That command started the following sequence:

  1. The pasted command ran mshta, a legitimate Windows utility that can execute scripts.

  2. mshta retrieved JavaScript from an attacker-controlled server and ran it.

  3. The JavaScript launched PowerShell, the Windows scripting and command-line administration environment.

  4. PowerShell downloaded additional PowerShell code, which downloaded and launched the Lumma executable.

The visitor ran one command, but four components ran before the infostealer did.

The code that runs first may have to meet constraints the final payload doesn’t. An exploit may allow only a short fragment of machine code to be injected, while the payload needs much more code, configuration, or additional libraries.

Multi-stage delivery divides the work across a sequence of components. An initial component starts another, which may retrieve configuration, download more code, or prepare the final payload for execution.

The Morris worm’s buffer-overflow attack on fingerd worked this way. The injected code did only one thing: it started a shell. Through that shell, the worm sent a small C program, which connected back to the infecting machine and retrieved the full worm. The complete worm did not have to fit inside the injected code.

Separating the components also allows an operator to select a payload for the target’s operating system and processor architecture. An operator can replace a payload stored on a server without changing the original lure, and the same delivery code can carry different payloads. Legitimate network installers use similar methods to select compatible packages and retrieve updated versions.

Each required download creates an opportunity to interrupt delivery. Blocking a server or its network connection may stop the chain before the payload arrives. New process launches and network connections also provide events that defenders can inspect.

Several delivery components may only get a payload to run. Persistence, which allows malware to remain available or restart later, and communication with an operator are separate functions. One program can perform these functions, but an infostealer may instead collect browser data and exit without creating any startup entry.

Droppers and Downloaders

A first component often has only enough functionality to install the component the attacker actually wants.

A dropper contains another payload and places it on the target. The embedded payload might be stored inside the dropper as ordinary data, as an archive, or in encoded form. Packaging the two together allows delivery even if the machine cannot immediately reach another server.

A downloader retrieves a later component from a network location. The initial file can remain small, and the operator can change the downloaded component independently. The network request also gives defenders an opportunity to block delivery before the next component runs.

The two are not exclusive. A program can contain one embedded component and download another. Security companies may classify it as a dropper in one report and a downloader in another.

Loaders and Process Injection

A downloaded executable still has to be loaded into a process’s memory and prepared for execution.

A loader prepares and starts another component. The operating system’s normal loader maps an executable and its required libraries into a process. A malicious loader may also decode a payload directly in memory, allowing it to run without saving the decoded payload as a standalone executable file. The payload can run in the loader’s own process or inside another process.

Process injection causes another process to execute code supplied by the attacker. One form, process hollowing, creates a process from a legitimate executable in a suspended state before its normal code runs. The loader replaces the program’s code in memory with malicious code and then resumes execution. Microsoft described the Lumma loader as capable of using process hollowing inside legitimate Windows processes.

A process list can still show the legitimate program’s name even though the process now executes the attacker’s instructions. The signed executable on disk can remain unchanged. Its signature covers that file, including its original code, but does not cover replacement code introduced into the process’s memory.

The operating system restricts which processes a loader can modify. On Windows, writing another process’s memory or changing its execution requires specific access rights. Linux similarly checks access to ptrace, an interface debuggers use to inspect and modify another process. A loader can create a child process that it is permitted to modify, then arrange for its payload to run there. The child normally runs with the loader’s existing permissions.

Executing Malware from Memory

A final payload does not have to be saved as an ordinary executable file, since an exploit, script interpreter, or loader can prepare its instructions directly in memory.

Fileless malware is a broad label for execution that does not depend on a conventional payload executable on disk. The term includes memory-resident payloads, interpreter-based execution, and instructions stored in configuration data. A delivery chain can start with a file and still end in a memory-only stage, so the label doesn’t describe every component.

Memory is volatile, so code that runs only in RAM disappears on a full reboot. Persistence requires storing something elsewhere to reload or reconstruct the code, such as a startup entry that retrieves a script; without that mechanism, the attacker must re-execute it. Interpreters may also leave cached scripts, command history, logs, or data written from memory to a paging file, even when the final payload was never written to disk as an executable.

Living off the land uses programs and services already present on a system for the attacker’s own purposes. PowerShell and legitimate remote-management utilities provide capabilities useful to both administrators and attackers, and this technique appears in file-based and fileless attacks.

A valid signature on an interpreter authenticates the interpreter itself, but not a script supplied later. Detection therefore also needs information about the script, its origin, the process that started the interpreter, and the resources accessed, since the same signed program can execute a scheduled backup or a script that steals a browser profile.

Packers

A scanner looking for known malicious instruction sequences may miss them when an executable stores its code in compressed or encrypted form.

A packer transforms an executable, typically by compressing or encrypting its program code. The packed executable contains compressed or encrypted program code and a startup routine that restores it in memory at runtime. When the executable launches, that routine decompresses or decrypts the program code into memory and transfers execution to it.

Legitimate developers also use packing to reduce executable size or make commercial code harder to inspect. For example, UPX is an open-source executable compressor whose output remains directly runnable.

Analysts can use a debugger or sandbox to observe unpacking and inspect the restored code in memory. Some malware scanners also unpack common formats before scanning or recognize the unpacking code itself.

Interpreting a Delivery Chain

The following table separates each component’s delivery role from the trust boundary it crosses in a script-based Lumma chain. The number and arrangement of components vary among campaigns:

Component Role in delivery Trust boundary crossed
A fake verification page Persuades the user to initiate execution. A web page gains influence over an operating system action.
A command launched by the user Starts a legitimate interpreter that retrieves attacker-selected code. Untrusted text becomes an instruction under the user’s identity.
A script or intermediate program Downloads and prepares a later component. A network response becomes locally executable content.
A loader Starts the credential-stealing payload, possibly inside another process. The visible program identity may differ from the code executing.

The attacker-supplied scripts and loader are malware, even though they don’t steal anything themselves: each exists to run code the user didn’t authorize. They use legitimate Windows utilities, mshta and PowerShell, to execute.

Persistence

Malware can create a scheduled task that launches its executable whenever a user logs in. Terminating the malicious process stops the current execution, but the task remains and launches the program again at the next login.

Persistence ensures malicious code runs again after process termination, logout, or reboot. It combines stored code or configuration with an event that triggers it, such as system boot, user login, or application startup.

Persistence mechanisms fall into three groups:

  1. Operating-system and application persistence uses services, login items, scheduled tasks, shell profiles, document templates, and library loading.

  2. Boot-code persistence changes startup code on disk: the boot sector on early PCs or boot-loader files on modern ones. The malicious code runs before the operating system.

  3. Firmware persistence changes the platform firmware in nonvolatile storage. The malicious code runs before the boot code on disk.

Registering Code to Run Again

An attacker changes startup configuration or application files so that ordinary software restarts the malicious code. These mechanisms do not require a kernel exploit.

Startup entries specify what should run and which event should start it. For example, the Windows registry, a database of system and application settings, contains Run keys that list programs to start when a user logs in. The following table groups common registration points by the event that activates them:

Code runs when… Windows Linux macOS
The system boots Windows service systemd unit or init script launchd daemon
A user logs in Registry Run key, Startup folder Desktop autostart entry launchd agent, login item
A scheduled time or event arrives Scheduled task cron job, systemd timer cron job, launchd agent
The user opens a shell PowerShell profile .bashrc, .profile .zshrc, .profile
An application starts Office template, browser extension Browser extension Office template, browser extension
A program loads a library DLL search-order hijacking Startup configuration using LD_PRELOAD, library path hijacking Dynamic library hijacking

User-level persistence uses settings the user controls, such as personal login items and shell profiles, and runs the code with that user’s permissions. System-level persistence uses machine-wide services or startup settings and requires access to protected system configuration or program files. A service or scheduled task runs under its configured account.

In library hijacking, a program loads a library by name, and the attacker places a malicious library with that name in a directory the program searches first. Windows calls this DLL hijacking. A DLL (dynamically linked library) is the Windows form of a shared library.

Document applications have their own loading mechanisms. A macro virus can hide in a template the application loads when opening unrelated documents, and a scan limited to operating system startup entries misses that activation source.

Deleting a payload but leaving the downloader’s scheduled task can cause another download. Removing a startup entry does not stop a process that is already running. Cleaning the machine requires inspecting both the running program and the settings that will restart it.

Infecting Firmware and Boot Code

Malware that runs before the operating system can change code that the system will later execute.

At power-on, a PC begins executing firmware, software in nonvolatile storage that initializes or controls hardware. The platform firmware initializes the hardware needed for startup and starts a boot loader, a program that loads the operating system kernel into memory and transfers control to it. The kernel then initializes the operating system and starts the user-space processes that launch services and applications.

On a modern PC, the Unified Extensible Firmware Interface (UEFI) specifies the interface between platform firmware and the operating system’s startup environment. It succeeded the BIOS, the older PC firmware interface. An attacker can target the firmware itself or the boot-loader files on disk, which can provide execution before the kernel’s protections and ordinary security software are active.

Infecting Boot Code on Disk

A bootkit modifies or abuses disk startup code to run before the operating system and maintain access. On UEFI-based PCs, it installs or modifies boot-loader files on the EFI System Partition, the disk partition reserved for startup software.

On BIOS-based PCs, firmware starts code in a disk’s boot sector, a block containing startup instructions. A boot sector virus infects this code and spreads to other disks, including hard disks and removable disks such as floppies.

Early execution lets a bootkit modify later code in memory, including code that has a valid signature on disk. Installing it requires write access to the startup components or a vulnerability that permits the change.

Infecting Firmware

A firmware implant modifies code or data in the platform firmware’s nonvolatile storage. Replacing the startup disk removes a disk-based bootkit, while a firmware implant survives because the malicious change is stored in firmware memory.

Verifying the Boot Process

A compromised boot loader can alter the operating system that starts after it and interfere with that system’s checks. Modern PCs use three mechanisms to protect and verify startup:

  1. Secure Boot checks that boot software is authorized before it runs.

  2. Measured boot records hashes of boot code and settings in protected hardware.

  3. Remote attestation lets another system verify the record and decide whether the boot state is approved.

The mechanisms below are the PC versions. Macs, iPhones, and Android phones each have their own versions of the same three steps under different names.

Blocking Unauthorized Boot Code with Secure Boot

The next component in the boot sequence should not gain execution merely because it occupies the expected file or disk partition.

Secure Boot checks boot software against an authorization policy before permitting it to execute. The UEFI specification defines the firmware’s checks, and both Windows and Linux can boot with Secure Boot enabled. The firmware contains trusted public keys or certificates and a list of revoked components, so verification can accept code signed by an approved authority while rejecting revoked components.

A signature authenticates the signed code, but that code can still contain a vulnerability. Installing a fixed boot loader does not prevent an attacker from supplying an older signed copy if policy still permits it, so defending against that replacement also requires revoking the vulnerable boot components.

Revocation can also affect legitimate recovery media that contains an older boot loader. Administrators have to update that media before relying on it to recover a machine whose policy rejects the old component.

Recording Boot Measurements with a TPM

Secure Boot authorizes startup code. To verify what actually booted, the system needs a record the operating system cannot rewrite.

A Trusted Platform Module (TPM) is a hardware-backed cryptographic component that stores keys, signs data, and maintains a protected record of boot measurements. Software requests signatures without receiving the TPM’s private signing key.

Measured boot records hashes of boot code and settings in the TPM. Firmware and boot software measure components before executing them. The TPM accumulates these measurements in a protected hash chain that the operating system cannot rewrite. The record depends on trusted early firmware supplying correct measurements.

Using the Measurements

BitLocker, Windows’s disk-encryption system, uses these measurements to let the TPM release a decryption key only for an approved boot state.

Remote attestation lets another system check a device’s recorded boot state before granting access:

  1. The service sends a fresh random challenge to prevent reuse of an old report.

  2. The TPM signs its current measurement record and the challenge with its private key.

  3. The service verifies the signature and challenge, then compares the measurements with its approved boot states.

Suppose an attacker installs an older signed boot loader that Secure Boot still accepts. Measured boot records the older version’s hash, and a service configured to accept the current version rejects the device. The TPM’s signature still verifies: it proves the report is authentic, while the measurement comparison determines whether the boot state is approved.

These measurements describe startup. Detecting an attack that compromises the kernel later requires monitoring the running system.

Concealing Malware with Rootkits

A malicious file can remain on disk while directory listings omit it because the attacker changes the software that provides the listings instead of removing the file.

A rootkit conceals unauthorized activity or access by altering system programs, libraries, or the kernel. It can hide files, processes, connections, and configuration changes, and it may hide a backdoor while a startup service arranges for that backdoor to launch again.

Altering User-space Programs and Libraries

A user-mode rootkit changes programs or libraries outside the kernel to conceal an intrusion. For example, a modified ls hides selected filenames, while a modified ps hides selected processes. Traditional Unix rootkits bundled these replacements with backdoors, such as a modified login that accepted an attacker-chosen password. The name rootkit refers to a toolkit for hiding an intrusion and maintaining root-level access.

Programs that use shared libraries obtain many functions from those libraries. A rootkit can replace a library file on disk with a modified version. A malicious directory-reading function in that library can filter out selected filenames, affecting otherwise unmodified programs that call it.

Library interposition substitutes another implementation of a function without replacing the original library file. On Linux, LD_PRELOAD instructs the dynamic loader to load an additional library before ordinary dependencies, allowing its functions to override the usual implementations. The original library remains unchanged on disk.

Replacing a protected system library requires privileged write access. An ordinary user can preload a library into a program running under that user’s account. Linux restricts untrusted preloading for privileged executables, so LD_PRELOAD alone does not grant root access.

If the kernel hasn’t changed, it still maintains the underlying process and file information, so a trusted program that avoids the substituted utility or library can expose the discrepancy.

Hiding Activity in the Kernel

A kernel-mode rootkit changes the kernel, often through a driver or kernel module. It can filter the file and process information returned through system calls or /proc, Linux’s virtual filesystem for kernel and process information. Unmodified utilities then display the filtered results.

Another technique alters kernel data structures. A rootkit can remove a process from the list used to generate a process listing while leaving the scheduler’s records intact. The process continues to run even though it is absent from that listing.

Kernel execution normally requires authority to load a driver or a vulnerability that permits kernel modification, and a downloaded application running as an ordinary user does not automatically have that access. Once the kernel is compromised, security software that depends on its reports can receive falsified information or be disabled.

Defending against Rootkits

Rootkit detection requires an inspection method that avoids the software the rootkit changed. For a user-mode rootkit, this can be a trusted copy of the altered program or an inspection tool that doesn’t load the altered library.

Kernel-rootkit investigations use two approaches:

  1. Offline inspection. Booting from trusted recovery media allows an investigator to inspect the disk without starting the installed kernel. The rootkit’s code is not running, so it cannot filter the inspection results.

  2. Cross-view detection. An inspection tool compares ordinary system listings with lower-level reads of disk and registry structures. Microsoft’s RootkitRevealer ran inside Windows and used this method. A file omitted from a directory listing but still recorded in the file system’s metadata is a discrepancy to investigate. A kernel rootkit can also interfere with lower-level reads, so matching results don’t prove the system is clean.

Memory contents are lost at shutdown, so an investigation may also need a memory copy captured before rebooting into recovery media.

Requiring signed drivers restricts what can load into the kernel, but a signature doesn’t prove a driver is harmless. Stuxnet used drivers signed using compromised code-signing private keys belonging to Realtek and JMicron, companies that make computer components. An attacker can also load a legitimate signed driver and exploit a vulnerability in it, which is why Microsoft maintains a blocklist of known vulnerable drivers whose signatures are still valid.

Malware on Mobile Devices

Phones isolate apps and mediate access to shared resources through permissions, as described in the containment notes.

Abusing App Permissions

Malware can arrive as a deliberately malicious app or as a trojanized application, a useful program altered to include hidden behavior. A compromised update or dependency can add that behavior after an initially benign installation, and the attacker can use the app’s useful purpose to obtain permission for unrelated collection.

Mobile malware can misuse a granted capability without defeating app isolation: an app permitted to read contacts can copy contacts, and an app with notification access can observe information exposed there.

On Android, an accessibility service assists users with disabilities by reading screen content and operating interface controls. Granting those capabilities to a malicious service lets it observe or interact with other apps without escaping the sandbox. App-store review attempts to reject such behavior before distribution, while the sandbox continues to restrict the app’s other access.

Exploiting Existing Software

An attacker can also exploit software already on the phone. In a 2021 Pegasus spyware attack delivered through iMessage, a malicious attachment triggered an integer-overflow flaw in an image decoder, allowing attacker-controlled code to run in a message-processing component. A separate exploit then let that code escape the component’s sandbox. The attack required no app installation, permission approval, or message interaction.


Next: Part 4: Triggers, Communication, Payloads, and Detection


Lecture 9: Part 1 | Part 2 | Part 3 | Part 4
Lecture 9 Study Guide | List of terms