<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged dfir</title>
<link>https://coderlegion.com/tag/dfir</link>
<description>Powered by Question2Answer</description>
<item>
<title>Built an Autonomous DFIR Agent SIFT-AEGIS — Here's What I Learned</title>
<link>https://coderlegion.com/20700/built-an-autonomous-dfir-agent-sift-aegis-heres-what-i-learned</link>
<description>&lt;p&gt;I just submitted to the SANS Find Evil! Hackathon and wanted to share what I built: SIFT-AEGIS, a fully autonomous Digital Forensics and Incident Response agent that investigates a real criminal case end-to-end without human intervention.&lt;br&gt;
&lt;strong&gt;The Problem&lt;/strong&gt;&lt;br&gt;
DFIR investigations are slow and analyst-dependent. A skilled investigator working the M57-Patents case (real NIST CFReDS corporate espionage scenario) might take hours to correlate memory artifacts, disk evidence, emails, and browser history into a coherent theory. I wanted to know: can an AI agent do this autonomously with the same rigor — and prove it with ground-truth benchmarks?&lt;br&gt;
What I Built&lt;br&gt;
SIFT-AEGIS runs on a SANS SIFT Workstation and connects to a custom Model Context Protocol (MCP) server with 20 read-only forensic tools wrapping Volatility3. The key architectural decision: the write-block is architectural, not prompt-based. The MCP server has zero write/delete/shell tools. The agent physically cannot destroy evidence.&lt;br&gt;
&lt;strong&gt;The investigation pipeline:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Iteration 1 — Memory forensics (process list, malfind injection detection, network connections, registry persistence)&lt;br&gt;
Iteration 2 — Disk forensics (MFT timeline, email artifacts, browser history, document staging)&lt;br&gt;
Iteration 3 — Cross-correlation, theory finalization, disconfirmation searches&lt;/p&gt;
&lt;p&gt;The Competing Case Theory Engine simultaneously scores three theories — Insider Threat, Authorized Business Research, Accidental Activity — updating confidence as evidence accumulates. Theory evolution: 70% → 99% across iterations.&lt;br&gt;
Honest Benchmark Results&lt;br&gt;
Against the 10-item M57-Patents ground truth:&lt;/p&gt;
&lt;p&gt;Disk-Layer Precision: 1.0 (zero false positives)&lt;br&gt;
Disk-Layer Recall: 0.8 (8/10 ground truth items found)&lt;br&gt;
Disk-Layer F1: 0.8889&lt;br&gt;
Hallucination Rate: 0.0&lt;br&gt;
Total tool calls: 33&lt;br&gt;
Self-corrections applied: 45&lt;br&gt;
Runtime: ~6 minutes&lt;/p&gt;
&lt;p&gt;I report two scores transparently — disk-layer only and full-finding (which includes memory injection artifacts that have no ground truth entry). Most submissions would hide the lower number. I reported both and documented why.&lt;br&gt;
&lt;strong&gt;Biggest Technical Challenges&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Volatility3 output noise — raw output has stderr, progress bars, and warnings that corrupted MCP responses. Fixed with a stdio wrapper.&lt;br&gt;
Ground truth scope mismatch — M57 ground truth covers disk only. Memory injection findings (csrss.exe, winlogon.exe) are real artifacts but score as false positives. Solved with dual-score methodology.&lt;br&gt;
Self-correction memoization — early iterations re-ran failing tools repeatedly. Fixed with a persistent failure tracker that marks degraded tools and routes to alternatives.&lt;br&gt;
Autonomous polling — getting the agent to monitor a 6-minute investigation without human prompting required careful tool return message design.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What I'd Do Differently&lt;/strong&gt;&lt;br&gt;
Start with ground truth benchmarking on day 1, not day 3. The benchmark revealed gaps that shaped the entire architecture — if I'd built it first I'd have made better tool coverage decisions earlier.&lt;br&gt;
&lt;strong&gt;Stack&lt;/strong&gt;&lt;br&gt;
Python, FastMCP, Volatility3, OpenClaw, Gemini API, Google ADK, SIFT Workstation&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;GitHub: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/ssurekumar01111-hue/sift-aegis&quot;&gt;https://github.com/ssurekumar01111-hue/sift-aegis&lt;/a&gt;&lt;br&gt;
Demo: &lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/iaf47TIKkLw&quot;&gt;https://youtu.be/iaf47TIKkLw&lt;/a&gt;&lt;br&gt;
Devpost: &lt;a rel=&quot;nofollow&quot; href=&quot;https://devpost.com/software/sift-aegis&quot;&gt;https://devpost.com/software/sift-aegis&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Built solo in 4 days. Happy to answer questions about the MCP architecture, the self-correction loop, or the benchmarking methodology.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/20700/built-an-autonomous-dfir-agent-sift-aegis-heres-what-i-learned</guid>
<pubDate>Tue, 16 Jun 2026 11:14:16 +0000</pubDate>
</item>
<item>
<title>Ursnif Malware — Reconstructing a 6-Stage Infection Chain from a PCAP</title>
<link>https://coderlegion.com/14178/ursnif-malware-reconstructing-a-6-stage-infection-chain-from-a-pcap</link>
<description>&lt;p&gt;date: 2026-03-20&lt;br&gt;
description: A walkthrough of my first real malware PCAP investigation — how Ursnif used .avi file extensions to disguise DLL payloads, TLS C2 beaconing, and how I mapped the full attack to MITRE ATT&amp;amp;CK with Splunk detection rules.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;One of the most powerful skills a SOC analyst can develop is the ability to look at a packet capture and reconstruct exactly what an attacker did — step by step, packet by packet.&lt;/p&gt;
&lt;p&gt;This write-up walks through my first real PCAP investigation using a controlled Ursnif/Gozi banking trojan dataset from &lt;a rel=&quot;nofollow&quot; href=&quot;https://malware-traffic-analysis.net&quot;&gt;malware-traffic-analysis.net&lt;/a&gt; — a site widely used in the security community for analyst training.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; 6-stage infection chain reconstructed · 10 IOCs extracted · 5 Splunk detection rules written — from 2,180 packets.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-is-ursnif-&quot;&gt;What is Ursnif?&lt;/h3&gt;
&lt;p&gt;Ursnif (also known as Gozi or ISFB) is one of the oldest banking trojans documented in the wild. Key characteristics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Delivered via malicious Office document macros&lt;/li&gt;
&lt;li&gt;Multi-stage payload delivery using disguised file extensions&lt;/li&gt;
&lt;li&gt;Encrypted C2 communication over TLS&lt;/li&gt;
&lt;li&gt;Modular credential theft and web injection&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;tools-used&quot;&gt;Tools Used&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Wireshark 4.x&lt;/strong&gt; — packet analysis&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VirusTotal&lt;/strong&gt; — hash and domain reputation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MITRE ATT&amp;amp;CK Navigator&lt;/strong&gt; — TTP mapping&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Splunk SPL&lt;/strong&gt; — detection rule development&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-1-getting-bearings&quot;&gt;Step 1 — Getting Bearings&lt;/h3&gt;
&lt;p&gt;Before any filters, I start with Statistics.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Statistics → Conversations → TCP&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ozq7sc5q5247m1qqzhpl.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;External IP &lt;/th&gt;&lt;th&gt;Packets &lt;/th&gt;&lt;th&gt;Role (discovered later)&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;46.102.153.16 &lt;/td&gt;&lt;td&gt;906 &lt;/td&gt;&lt;td&gt;Payload server&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;68.168.123.78 &lt;/td&gt;&lt;td&gt;18 &lt;/td&gt;&lt;td&gt;C2 server&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;217.138.205.170 &lt;/td&gt;&lt;td&gt;8 &lt;/td&gt;&lt;td&gt;Initial C2&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Statistics → Protocol Hierarchy:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TCP: 97.9%&lt;/li&gt;
&lt;li&gt;TLS: 8.2% — encrypted C2 and payload retrieval&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/la9o2n4z9xkipmwpchm5.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-2-identifying-the-victim&quot;&gt;Step 2 — Identifying the Victim&lt;/h3&gt;
&lt;p&gt;Filter: &lt;code&gt;dhcp&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4jws3ry52tbycx68z0eq.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Field &lt;/th&gt;&lt;th&gt;Value&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;IP Address &lt;/td&gt;&lt;td&gt;10.2.24.101&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;MAC Address &lt;/td&gt;&lt;td&gt;00:08:02:1c:47:ae&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-3-the-full-infection-chain&quot;&gt;Step 3 — The Full Infection Chain&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gjt2rrbgntmovsxi3q3i.jpeg&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;stage-1-initial-compromise&quot;&gt;Stage 1 — Initial Compromise&lt;/h4&gt;
&lt;p&gt;Consistent with Ursnif's delivery: malicious Excel file with embedded VBA macro. Not captured in PCAP.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MITRE:&lt;/strong&gt; &lt;code&gt;T1566.001&lt;/code&gt; &lt;code&gt;T1059.005&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;stage-2-first-tls-contact-packet-10-&quot;&gt;Stage 2 — First TLS Contact (Packet 10)&lt;/h4&gt;
&lt;p&gt;Filter: &lt;code&gt;ssl.handshake.type==1&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Source:      10.2.24.101
Destination: 217.138.205.170
SNI:         fatturapagamentodi.pw
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rzxy982ld5i6bny563dd.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;The SNI field in a TLS Client Hello is &lt;strong&gt;plaintext&lt;/strong&gt; — even without decrypting traffic, we can see exactly which domain the malware contacted.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MITRE:&lt;/strong&gt; &lt;code&gt;T1071.003&lt;/code&gt; &lt;code&gt;T1573.001&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;stage-3-payload-download-packets-218-1116-&quot;&gt;Stage 3 — Payload Download (Packets 218–1116)&lt;/h4&gt;
&lt;p&gt;Filter: &lt;code&gt;http.request.method == &quot;GET&quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ykmt9gt8xgixbcrccqq.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;906 packets to 46.102.153.16. Files had &lt;code&gt;.avi&lt;/code&gt; extensions — but were Ursnif DLL segments reassembled in memory. The &lt;code&gt;.avi&lt;/code&gt; extension bypasses firewalls that block &lt;code&gt;.exe&lt;/code&gt; and &lt;code&gt;.dll&lt;/code&gt; downloads.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MITRE:&lt;/strong&gt; &lt;code&gt;T1105&lt;/code&gt; &lt;code&gt;T1027&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;stage-4-secondary-payload-packets-1299-1563-&quot;&gt;Stage 4 — Secondary Payload (Packets 1299, 1563)&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;GET /grabb32.rar  →  37.10.71.149
GET /grabb64.rar  →  37.10.71.149
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both 32-bit and 64-bit binaries from a separate server.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;stage-5-c2-beaconing-packets-1215-2167-&quot;&gt;Stage 5 — C2 Beaconing (Packets 1215–2167)&lt;/h4&gt;
&lt;p&gt;Filter: &lt;code&gt;ssl.handshake.type==1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/on32qknfcp1bpyxty8z2.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Destination: 68.168.123.78
SNI:         asistenzaonline.xyz
Interval:    ~5–10 minutes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Human browsing is random and bursty. &lt;strong&gt;Malware is a clock.&lt;/strong&gt; The consistent interval between TLS handshakes is machine behavior — your detection signal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MITRE:&lt;/strong&gt; &lt;code&gt;T1071.001&lt;/code&gt; &lt;code&gt;T1573.001&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;stage-6-persistence-inferred-&quot;&gt;Stage 6 — Persistence (Inferred)&lt;/h4&gt;
&lt;p&gt;Registry injection under &lt;code&gt;HKCU\Software\[random key]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MITRE:&lt;/strong&gt; &lt;code&gt;T1547.001&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;indicators-of-compromise&quot;&gt;Indicators of Compromise&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Type &lt;/th&gt;&lt;th&gt;Value &lt;/th&gt;&lt;th&gt;Role&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;IP &lt;/td&gt;&lt;td&gt;217.138.205.170 &lt;/td&gt;&lt;td&gt;Initial C2&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;IP &lt;/td&gt;&lt;td&gt;46.102.153.16 &lt;/td&gt;&lt;td&gt;Payload server&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;IP &lt;/td&gt;&lt;td&gt;37.10.71.149 &lt;/td&gt;&lt;td&gt;Secondary payload&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;IP &lt;/td&gt;&lt;td&gt;68.168.123.78 &lt;/td&gt;&lt;td&gt;Primary C2&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Domain &lt;/td&gt;&lt;td&gt;fatturapagamentodi.pw &lt;/td&gt;&lt;td&gt;Stage 2 C2&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Domain &lt;/td&gt;&lt;td&gt;asistenzaonline.xyz &lt;/td&gt;&lt;td&gt;Persistent C2&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Domain &lt;/td&gt;&lt;td&gt;pizdelko.xyz &lt;/td&gt;&lt;td&gt;Fallback C2&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;URL &lt;/td&gt;&lt;td&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;http://46.102.153.16/*.avi&quot;&gt;http://46.102.153.16/*.avi&lt;/a&gt; &lt;/td&gt;&lt;td&gt;DLL delivery&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;File &lt;/td&gt;&lt;td&gt;grabb32.rar &lt;/td&gt;&lt;td&gt;32-bit binary&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;File &lt;/td&gt;&lt;td&gt;grabb64.rar &lt;/td&gt;&lt;td&gt;64-bit binary&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;mitre-att-amp-ck-map&quot;&gt;MITRE ATT&amp;amp;CK Map&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;ID &lt;/th&gt;&lt;th&gt;Technique&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;T1566.001 &lt;/td&gt;&lt;td&gt;Phishing: Spearphishing Attachment&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;T1059.005 &lt;/td&gt;&lt;td&gt;Command and Scripting Interpreter: VBA&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;T1105 &lt;/td&gt;&lt;td&gt;Ingress Tool Transfer&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;T1071.001 &lt;/td&gt;&lt;td&gt;Application Layer Protocol: Web Protocols&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;T1071.003 &lt;/td&gt;&lt;td&gt;Application Layer Protocol: Mail Protocols&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;T1547.001 &lt;/td&gt;&lt;td&gt;Boot/Logon Autostart: Registry Run Keys&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;T1027 &lt;/td&gt;&lt;td&gt;Obfuscated Files or Information&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;T1573.001 &lt;/td&gt;&lt;td&gt;Encrypted Channel: Symmetric Cryptography&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;detection-rules-splunk-spl-&quot;&gt;Detection Rules (Splunk SPL)&lt;/h3&gt;
&lt;h4 id=&quot;rule-1-tls-to-suspicious-sni&quot;&gt;Rule 1 — TLS to suspicious SNI&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-splunk&quot;&gt;index=network ssl.handshake.type=1
| stats count by src_ip, ssl.handshake.extensions_server_name
| where NOT ssl.handshake.extensions_server_name LIKE &quot;%.google.com&quot;
  AND NOT ssl.handshake.extensions_server_name LIKE &quot;%.microsoft.com&quot;
  AND NOT ssl.handshake.extensions_server_name LIKE &quot;%.cloudflare.com&quot;
| sort -count
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;rule-2-c2-beaconing-via-tls-regularity&quot;&gt;Rule 2 — C2 beaconing via TLS regularity&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-splunk&quot;&gt;index=network ssl.handshake.type=1
| bucket _time span=10m
| stats count by src_ip, dest_ip, _time
| streamstats window=6 current=t stdev(count) as regularity by src_ip, dest_ip
| where regularity &amp;lt; 1.5
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;rule-3-media-file-extension-from-non-cdn-ip&quot;&gt;Rule 3 — Media file extension from non-CDN IP&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-splunk&quot;&gt;index=network http.request.method=GET
| rex field=uri &quot;(?&amp;lt;ext&amp;gt;\.[a-z0-9]{2,4})$&quot;
| where ext IN (&quot;.avi&quot;,&quot;.mp4&quot;,&quot;.mp3&quot;)
| eval dest_is_cdn=if(match(dest_ip,&quot;^(151\.101|104\.16|172\.67)&quot;),1,0)
| where dest_is_cdn=0
| stats count by src_ip, dest_ip, uri, ext
| where count &amp;gt; 3
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;rule-4-chunked-download-from-single-external-ip&quot;&gt;Rule 4 — Chunked download from single external IP&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-splunk&quot;&gt;index=network http.request.method=GET
| stats dc(uri) as unique_files, count as total_requests by src_ip, dest_ip
| where unique_files &amp;gt; 3
| eval internal=if(match(dest_ip,&quot;^(10\.|192\.168\.)&quot;),1,0)
| where internal=0
| sort -total_requests
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;rule-5-suspicious-archive-by-name-pattern&quot;&gt;Rule 5 — Suspicious archive by name pattern&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-splunk&quot;&gt;index=network http.request.method=GET
  (uri=&quot;*.rar&quot; OR uri=&quot;*.zip&quot;)
| where NOT dest_ip LIKE &quot;192.168.%&quot; AND NOT dest_ip LIKE &quot;10.%&quot;
| eval suspicious=if(match(uri,&quot;(grabb|drop|stage|payload|inject)&quot;),1,0)
| where suspicious=1
| stats count by src_ip, dest_ip, uri
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;three-key-lessons&quot;&gt;Three Key Lessons&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; The &lt;code&gt;.avi&lt;/code&gt; trick works because defenders trust file extensions. Fix: inspect file content, not just extension.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; Beaconing regularity is your strongest behavioral detection signal. Low standard deviation in connection intervals = machine, not human.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; Multi-stage infrastructure creates more IOCs, not fewer. Four servers = four things to block and hunt for.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;immediate-response-actions&quot;&gt;Immediate Response Actions&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Isolate 10.2.24.101&lt;/li&gt;
&lt;li&gt;Block all 4 attacker IPs&lt;/li&gt;
&lt;li&gt;Sinkhole 3 C2 domains at DNS&lt;/li&gt;
&lt;li&gt;Hunt for &lt;code&gt;.avi&lt;/code&gt; GETs from non-CDN IPs in 30 days of proxy logs&lt;/li&gt;
&lt;li&gt;Deploy rules 1–5 to SIEM&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;full-report-on-github&quot;&gt;Full Report on GitHub&lt;/h3&gt;
&lt;p&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/himanshumodi3108/cybersec-portfolio&quot;&gt;github.com/himanshumodi3108/cybersec-portfolio&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This analysis was performed on a controlled training dataset from malware-traffic-analysis.net for educational purposes.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Himanshu Kumar Modi · Associate at PwC India · SOC Analyst in Training&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/himanshu-kumar-modi-063b88239&quot;&gt;LinkedIn&lt;/a&gt; · &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/himanshumodi3108/cybersec-portfolio&quot;&gt;Cybersecurity Portfolio&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/14178/ursnif-malware-reconstructing-a-6-stage-infection-chain-from-a-pcap</guid>
<pubDate>Tue, 07 Apr 2026 17:43:08 +0000</pubDate>
</item>
<item>
<title>Emotet + Cobalt Strike — Dissecting a Multi-Stage Attack in Wireshark</title>
<link>https://coderlegion.com/14177/emotet-cobalt-strike-dissecting-a-multi-stage-attack-in-wireshark</link>
<description>&lt;hr&gt;
&lt;p&gt;description: How I identified Cobalt Strike C2 servers using Host header masquerading detection, found 3 payload domains via time-bounded TLS SNI hunting, and traced a malspam campaign — all from a single PCAP in the TryHackMe Carnage room.&lt;br&gt;
tags: security, dfir, wireshark, tutorial&lt;br&gt;
cover_image:&lt;/p&gt;
&lt;h3 id=&quot;canonical-url-&quot;&gt;canonical_url:&lt;/h3&gt;
&lt;p&gt;The TryHackMe Carnage room presents one of the most realistic attack scenarios available for free — a complete Emotet infection chain followed by Cobalt Strike post-exploitation and a malspam campaign generating 1,439 SMTP packets.&lt;/p&gt;
&lt;p&gt;This write-up focuses on three techniques that make the biggest practical difference in real SOC investigations.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Dataset: TryHackMe Carnage room — controlled training environment.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;attack-chain-overview&quot;&gt;Attack Chain Overview&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Zip email → XLS macro → payload from 3 domains
      ↓
Cobalt Strike C2 (Host header spoofed as Verisign)
      ↓
Post-infection C2 → maldivehost.net
      ↓
IP recon → api.ipify.org (17:00:04 UTC)
      ↓
Malspam → 1,439 SMTP packets
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;technique-1-hex-inspection-for-zip-contents&quot;&gt;Technique 1 — Hex Inspection for Zip Contents&lt;/h3&gt;
&lt;p&gt;Filter: HTTP response for &lt;code&gt;documents.zip&lt;/code&gt; → View → Packet Bytes&lt;/p&gt;
&lt;p&gt;Zip central directories are stored at the end of the archive. Checking the hex from the bottom found &lt;code&gt;chart-1530076591.xls&lt;/code&gt; immediately — without downloading or executing anything.&lt;/p&gt;
&lt;p&gt;The 10-digit epoch timestamp filename is a known Emotet naming pattern. Recognizing this from hex alone is a core analyst skill.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; &lt;code&gt;chart-1530076591.xls&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;technique-2-time-bounded-tls-sni-hunting&quot;&gt;Technique 2 — Time-Bounded TLS SNI Hunting&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;tls.handshake.type == 1&lt;/code&gt; returned 181 Client Hello packets. Too many to check manually.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Use the infection timestamp as a filter boundary.&lt;/p&gt;
&lt;p&gt;First malicious HTTP: &lt;code&gt;16:44:38 UTC&lt;/code&gt; → narrow TLS filter to &lt;code&gt;16:45:11–16:45:30 UTC&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;181 packets → 5 packets. Each SNI checked on VirusTotal:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;finejewels.com.au&lt;/code&gt; → &lt;strong&gt;malicious&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;thietbiagt.com&lt;/code&gt; → &lt;strong&gt;malicious&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;new.americold.com&lt;/code&gt; → &lt;strong&gt;malicious&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The SNI field in TLS Client Hello is plaintext — you see the destination domain without decrypting anything. Time-bounding with the infection timestamp is what makes this technique practical.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MITRE:&lt;/strong&gt; &lt;code&gt;T1573.001&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;technique-3-cobalt-strike-host-header-masquerading&quot;&gt;Technique 3 — Cobalt Strike Host Header Masquerading&lt;/h3&gt;
&lt;p&gt;Filter: &lt;code&gt;ip.dst == 185.106.96.158 &amp;amp;&amp;amp; http&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;Host: oscp.verisign.com
Actual destination: 185.106.96.158 (Cobalt Strike C2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Host header claimed Verisign. The actual IP had nothing to do with Verisign.&lt;/p&gt;
&lt;p&gt;Cobalt Strike's malleable C2 profiles allow operators to set any HTTP header to any value — attackers routinely spoof trusted domains (&lt;code&gt;verisign.com&lt;/code&gt;, &lt;code&gt;microsoft.com&lt;/code&gt;, &lt;code&gt;windowsupdate.com&lt;/code&gt;) to blend with enterprise traffic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Detection:&lt;/strong&gt; Cross-reference Host domain with actual destination IP. Mismatch where Host is a trusted domain but IP is external and flagged = confirmed masquerading.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MITRE:&lt;/strong&gt; &lt;code&gt;T1036&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;technique-4-smtp-forensics&quot;&gt;Technique 4 — SMTP Forensics&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-wireshark&quot;&gt;frame contains &quot;MAIL FROM&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; &lt;code&gt;smtp contains &quot;FROM&quot;&lt;/code&gt; returned wrong results. Always search for the complete field name.&lt;/p&gt;
&lt;p&gt;1,439 SMTP packets from an internal host = machine enrolled in Emotet malspam botnet. The infected machine was sending phishing emails on the attacker's behalf.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MITRE:&lt;/strong&gt; &lt;code&gt;T1071.003&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;cobalt-strike-identification-full-workflow&quot;&gt;Cobalt Strike Identification — Full Workflow&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Filter &lt;code&gt;http.request.method == &quot;GET&quot;&lt;/code&gt; → Statistics → Conversations → TCP → sort by frequency&lt;/li&gt;
&lt;li&gt;Note top recurring external IPs&lt;/li&gt;
&lt;li&gt;Check each on VirusTotal → &lt;strong&gt;Community tab&lt;/strong&gt; (not just detection — community notes confirm Cobalt Strike)&lt;/li&gt;
&lt;li&gt;Cross-reference Host header with actual destination IP for masquerading&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;C2 servers:&lt;/strong&gt; &lt;code&gt;185.106.96.158&lt;/code&gt; (survmeter.live), &lt;code&gt;185.125.204.174&lt;/code&gt; (securitybusinpuff.com)&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;ioc-table&quot;&gt;IOC Table&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Type &lt;/th&gt;&lt;th&gt;Value &lt;/th&gt;&lt;th&gt;Role&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Domain &lt;/td&gt;&lt;td&gt;attirenepal.com &lt;/td&gt;&lt;td&gt;Initial zip&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;File &lt;/td&gt;&lt;td&gt;documents.zip → chart-1530076591.xls &lt;/td&gt;&lt;td&gt;Macro payload&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Domain &lt;/td&gt;&lt;td&gt;finejewels.com.au &lt;/td&gt;&lt;td&gt;Secondary payload&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Domain &lt;/td&gt;&lt;td&gt;thietbiagt.com &lt;/td&gt;&lt;td&gt;Secondary payload&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Domain &lt;/td&gt;&lt;td&gt;new.americold.com &lt;/td&gt;&lt;td&gt;Secondary payload&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;IP &lt;/td&gt;&lt;td&gt;185.106.96.158 &lt;/td&gt;&lt;td&gt;Cobalt Strike C2&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;IP &lt;/td&gt;&lt;td&gt;185.125.204.174 &lt;/td&gt;&lt;td&gt;Cobalt Strike C2&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Domain &lt;/td&gt;&lt;td&gt;maldivehost.net &lt;/td&gt;&lt;td&gt;Post-infection C2&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Email &lt;/td&gt;&lt;td&gt;farshin@mailfa.com &lt;/td&gt;&lt;td&gt;Malspam sender&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;mitre-att-amp-ck&quot;&gt;MITRE ATT&amp;amp;CK&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;ID &lt;/th&gt;&lt;th&gt;Technique&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;T1566.001 &lt;/td&gt;&lt;td&gt;Phishing: Spearphishing Attachment&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;T1059.005 &lt;/td&gt;&lt;td&gt;VBA Macro&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;T1105 &lt;/td&gt;&lt;td&gt;Ingress Tool Transfer&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;T1071.001 &lt;/td&gt;&lt;td&gt;Web Protocols&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;T1573.001 &lt;/td&gt;&lt;td&gt;Encrypted Channel&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;T1036 &lt;/td&gt;&lt;td&gt;Masquerading&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;T1016 &lt;/td&gt;&lt;td&gt;System Network Config Discovery&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;T1583 &lt;/td&gt;&lt;td&gt;Acquire Infrastructure&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;T1071.003 &lt;/td&gt;&lt;td&gt;Mail Protocols&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;detection-rules-splunk-spl--1&quot;&gt;Detection Rules (Splunk SPL)&lt;/h3&gt;
&lt;h4 id=&quot;rule-1-cobalt-strike-host-header-masquerading&quot;&gt;Rule 1 — Cobalt Strike Host Header Masquerading&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-splunk&quot;&gt;index=network http.request.method=GET
| where http.host LIKE &quot;%.verisign.com&quot;
    OR http.host LIKE &quot;%.microsoft.com&quot;
    OR http.host LIKE &quot;%.windowsupdate.com&quot;
| eval dest_internal=if(match(dest_ip,&quot;^(10\.|192\.168\.)&quot;),1,0)
| where dest_internal=0
| stats count by src_ip, dest_ip, http.host
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;rule-2-tls-to-suspicious-tld&quot;&gt;Rule 2 — TLS to Suspicious TLD&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-splunk&quot;&gt;index=network ssl.handshake.type=1
| eval tld=mvindex(split(ssl.handshake.extensions_server_name,&quot;.&quot;), -1)
| where tld IN (&quot;live&quot;,&quot;xyz&quot;,&quot;top&quot;,&quot;pw&quot;,&quot;online&quot;,&quot;site&quot;,&quot;club&quot;,&quot;icu&quot;)
| stats count by src_ip, ssl.handshake.extensions_server_name
| sort -count
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;rule-3-internal-malspam-detection&quot;&gt;Rule 3 — Internal Malspam Detection&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-splunk&quot;&gt;index=network sourcetype=stream:smtp
| stats count as smtp_count by src_ip
| where smtp_count &amp;gt; 50
| join src_ip [search index=network http.request.method=POST
    | stats count by src_ip]
| table src_ip, smtp_count, count
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;three-key-takeaways&quot;&gt;Three Key Takeaways&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Time-bound your TLS filters.&lt;/strong&gt; The infection timestamp reduces 181 packets to 5. Always know your starting timestamp before filtering TLS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cobalt Strike hides in the Host header.&lt;/strong&gt; Cross-referencing the Host domain with the actual destination IP catches it every time — no threat intel feed required.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1,439 SMTP packets means you're already losing.&lt;/strong&gt; The machine is sending phishing on behalf of the attacker. Detecting this early is why malspam volume thresholds matter in SIEM rules.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;full-report-on-github-1&quot;&gt;Full Report on GitHub&lt;/h3&gt;
&lt;p&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/himanshumodi3108/cybersec-portfolio&quot;&gt;github.com/himanshumodi3108/cybersec-portfolio&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;TryHackMe Carnage room — controlled training environment.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Himanshu Kumar Modi · Associate at PwC India · SOC Analyst in Training&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/himanshu-kumar-modi-063b88239&quot;&gt;LinkedIn&lt;/a&gt; · &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/himanshumodi3108/cybersec-portfolio&quot;&gt;Cybersecurity Portfolio&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/14177/emotet-cobalt-strike-dissecting-a-multi-stage-attack-in-wireshark</guid>
<pubDate>Tue, 07 Apr 2026 17:42:27 +0000</pubDate>
</item>
</channel>
</rss>