Chapter 25

Memory Forensics End-to-End

A complete, realistic walkthrough of a memory forensics investigation — from the initial alert through acquisition, Volatility analysis, finding the implant, extracting the C2 config, and correlating findings with disk artifacts to produce a coherent incident timeline.

Scenario

Alert fires at 03:47 UTC: svchost.exe (PID 2148) making periodic connections to 185.220.101.47:443. The connection pattern is consistent with Cobalt Strike beacon: connection every 60 seconds ± 10 seconds, 7KB average request size. EDR flagged it but didn't kill it (configured in monitor mode on servers). Your task: confirm it's a beacon, identify the injection target, extract the C2 configuration, determine how long it's been running, and scope whether other systems are compromised.

Step 1: Acquisition Sequence

PowerShellstep1-acquire.ps1
# Run remotely via PSRemoting or directly on the host
# Timeline: T+0 (alert at 03:47) → Start this at T+3 (03:50)

$host   = "FINANCE-SRV01"
$out    = "\\evidence-share\CASE-2026-009\$host"
$ts     = "20260920-0350"

# Live volatile capture first (2 min)
Invoke-Command -ComputerName $host -ScriptBlock {
    netstat -anob   | Out-File "C:\Temp\netstat-$using:ts.txt"
    tasklist /v     | Out-File "C:\Temp\tasks-$using:ts.txt"
    arp -a          | Out-File "C:\Temp\arp-$using:ts.txt"
}
Copy-Item "\\$host\C$\Temp\*-$ts.txt" -Destination $out

# Memory acquisition via Velociraptor (fleet-deployed) — remote trigger
# Or push WinPmem via PSRemoting:
Invoke-Command -ComputerName $host -ScriptBlock {
    & "\\tools-share\winpmem.exe" "C:\Temp\RAM-$using:ts.raw"
}
Copy-Item "\\$host\C$\Temp\RAM-$ts.raw" -Destination $out

# Get hash for chain of custody
$hash = Get-FileHash "$out\RAM-$ts.raw" -Algorithm SHA256
Write-Host "RAM image SHA256: $($hash.Hash)"

Step 2: Process Tree Analysis

Bashstep2-processes.sh
RAM="/evidence/CASE-2026-009/FINANCE-SRV01/RAM-20260920-0350.raw"
OUT="/cases/2026-009/memory"
mkdir -p $OUT

# Process tree
vol -f $RAM windows.pstree --output csv > $OUT/pstree.csv

# Look at svchost.exe instances specifically
vol -f $RAM windows.pstree | grep -A2 -B2 svchost

# Expected: services.exe → svchost.exe (normal)
# Suspicious: unusual parent for svchost

# Check PID 2148 command line
vol -f $RAM windows.cmdline --pid 2148
# Clean svchost will show: C:\Windows\system32\svchost.exe -k netsvcs -p -s ...
# If it shows just "svchost.exe" with no -k flag: process was hollowed or renamed

# Check parent-child for PID 2148
vol -f $RAM windows.pslist | grep " 2148 \| 2148$"

Step 3: Confirm Network Connection

Bashstep3-network.sh
RAM="/evidence/CASE-2026-009/FINANCE-SRV01/RAM-20260920-0350.raw"

# Confirm connection from PID 2148 to 185.220.101.47
vol -f $RAM windows.netscan | grep 2148
# Looking for: 2148 | svchost.exe | ESTABLISHED | x.x.x.x:XXXXX → 185.220.101.47:443

# If connection is active at time of imaging:
#   Proto | LocalAddr:Port | ForeignAddr:Port | State | PID | Owner
#   TCP   | 10.0.1.15:52341| 185.220.101.47:443| ESTABLISHED | 2148 | svchost.exe

# VT lookup on the C2 IP
# curl -s -H "x-apikey: VT_API_KEY" \
#   "https://www.virustotal.com/api/v3/ip_addresses/185.220.101.47" | \
#   python3 -m json.tool | grep "malicious\|last_analysis"

Step 4: Find Injected Code

Bashstep4-malfind.sh
RAM="/evidence/CASE-2026-009/FINANCE-SRV01/RAM-20260920-0350.raw"

# Run malfind specifically on PID 2148
vol -f $RAM windows.malfind --pid 2148 --dump --output-dir $OUT/malfind/ \
    --output csv > $OUT/malfind.csv

# Check results
cat $OUT/malfind.csv
# Expect: at least one entry with PAGE_EXECUTE_READWRITE + PrivateMemory=True
# and MZ/PE header in the hex dump column

# Extract hex header of the dump to confirm PE
xxd $OUT/malfind/pid.2148.*.dmp | head -3
# 4d 5a = MZ header = it's a PE (likely reflective DLL injection)

Step 5: Extract Beacon Configuration

Bashstep5-beacon-config.sh
# 1768.py (SentinelOne) — Cobalt Strike config extractor
# Also: CobaltStrikeParser from Didier Stevens

# Run against the malfind dump
python3 1768.py $OUT/malfind/pid.2148.*.dmp

# Expected output:
# Config block found at offset: 0x...
#   BeaconType:       HTTPS
#   Port:             443
#   SleepTime:        60000   (60 seconds)
#   MaxGetSize:       1048576
#   Jitter:           10      (10% jitter)
#   MaxDns:           255
#   C2Server:         185.220.101.47,/updates.php
#   UserAgent:        Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)
#   HttpPostUri:      /submit.php
#   Watermark:        305419896  ← identifies the C2 team server license
#
# The watermark can sometimes identify which threat actor purchased the license.
# The User-Agent "MSIE 10.0" on a Windows 2022 server is a detection evasion tell
# (IE 10 would never run on Server 2022).

Step 6: Timeline and Dwell Time

Bashstep6-dwell-time.sh
# Determine when the beacon first ran
# Amcache: look for svchost32.exe or whatever the renamed tool was
AmcacheParser.exe -f "$TRIAGE\Amcache.hve" --csv "$OUT\amcache" -i on
# → Look for suspicious entries created before 03:47 UTC today

# Prefetch: svchost.exe run times (may not have separate entry if injected into legit svchost)
PECmd.exe -d "$TRIAGE\Prefetch" --csv "$OUT\prefetch"
# → Check if any unusual executable has earliest run time predating alert

# Event Log: first connection from PID 2148's parent process
# When was PID 2148's parent (services.exe spawning svchost) created?
vol -f $RAM windows.pslist | awk '/2148/{print $5}' # CreateTime column

# Registry: check for persistence that established this beacon
vol -f $RAM windows.registry.printkey \
    --key "Software\Microsoft\Windows\CurrentVersion\Run"
vol -f $RAM windows.registry.printkey \
    --key "SYSTEM\CurrentControlSet\Services"

Synthesized Findings

FindingEvidence sourceImplication
Cobalt Strike beacon injected into svchost.exe PID 2148malfind (RWX PE in private memory) + 1768.py config extractionActive remote access to attacker C2 at 185.220.101.47
C2 communication at 60-second intervals, port 443netscan (ESTABLISHED connection) + beacon config (SleepTime: 60000)Attacker has interactive access capability
DA hash found in LSASS memory (pypykatz)LSASS dump analysisDomain compromise — entire domain must be treated as compromised
Beacon process first created at 2026-09-17 02:23 UTC (3 days before detection)Process CreateTime in memory + Amcache entry timestamp3-day dwell time — attacker had extended access window
Scheduled task "WindowsUpdate_7a2f" in registry (SYSTEM hive from memory)windows.registry.printkey on TaskScheduler keyPersistence mechanism — must be removed and system re-eradicated after credential rotation

Q & A

Q: You've found and documented the beacon. What happens next — do you kill it or leave it running?

This is a judgment call that depends on the investigation stage and business priorities. Arguments for leaving it: if the beacon is active and beaconing home, you can observe the C2 infrastructure (passive network monitoring may reveal other victims or infrastructure), and you can gather more forensic data from the active session. Arguments for killing it: every minute it runs is another minute the attacker may be issuing commands, exfiltrating data, or moving laterally. The standard approach for most investigations: isolate the host immediately (network containment) but don't kill the process or reimage yet — take additional memory images if needed, complete forensic collection, then proceed with eradication. "Controlled observation" of an active implant is occasionally justified in law enforcement-coordinated investigations but requires explicit authorization and is not standard corporate IR practice. When in doubt, isolate and eradicate.