<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[FMI Cyber Security Consulting Services - Medium]]></title>
        <description><![CDATA[FMI Cyber Security Consulting Services is a division under PT. FPT Metrodata Indonesia and part of Metrodata Group. FMI Cyber Security Consulting Services provide following services : VAPT, Red Teaming, DFIR Services, MSS SOC, Training, and other cyber security fields. - Medium]]></description>
        <link>https://medium.com/fmisec?source=rss----5aebc5961dd0---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>FMI Cyber Security Consulting Services - Medium</title>
            <link>https://medium.com/fmisec?source=rss----5aebc5961dd0---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 06 Nov 2026 05:23:00 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/fmisec" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Hunting on the Wire : A Practical Guide to Network Threat Hunting — Part 3]]></title>
            <link>https://medium.com/fmisec/hunting-on-the-wire-a-practical-guide-to-network-threat-hunting-part-3-9aa997edc3e4?source=rss----5aebc5961dd0---4</link>
            <guid isPermaLink="false">https://medium.com/p/9aa997edc3e4</guid>
            <category><![CDATA[network-forensics]]></category>
            <category><![CDATA[network-threat-hunting]]></category>
            <category><![CDATA[threat-hunting-lab]]></category>
            <category><![CDATA[threat-hunting]]></category>
            <category><![CDATA[blue-team]]></category>
            <dc:creator><![CDATA[Digit Oktavianto]]></dc:creator>
            <pubDate>Wed, 04 Nov 2026 02:01:02 GMT</pubDate>
            <atom:updated>2026-11-04T02:01:02.763Z</atom:updated>
            <content:encoded><![CDATA[<p><em>A free, practical setup — Zeek, tshark, RITA, Security Onion and Malcolm — plus real datasets and five exercises to turn theory into muscle memory.</em></p><p><em>This is the final part of a three-part series. Part 1 explained why the network is the hunter’s ground truth. Part 2 walked through the four core hunts: long connections, lateral movement, beacons, protocol anomalies, DNS and outliers. Now we practise.</em></p><p>Threat hunting is a skill, and skills come from repetition. The good news is that network hunting is one of the cheapest disciplines to practise: everything in this article is free or open source, it runs on a laptop, and there are years of real malicious traffic published for exactly this purpose.</p><h3>Pick your lab architecture</h3><p>There are three sensible levels. Start at the first and grow.</p><p><strong>Level 1 — the analyst workstation (an afternoon).</strong> A single Linux VM (Ubuntu or Debian, 4 vCPU, 8 GB RAM, 100 GB disk) with:</p><ul><li><strong>Zeek</strong> to turn PCAPs into logs</li><li><strong>tshark / Wireshark</strong> for packet-level validation</li><li><strong>RITA</strong> for beacon, long-connection and DNS scoring</li><li><strong>zeek-cut, awk, sort, uniq, datamash</strong> — the Unix toolbox from Part 2</li><li>Optionally <a href="https://zui.brimdata.io/"><strong>Zui</strong></a> (formerly Brim), a desktop app that loads a PCAP, runs Zeek and Suricata behind the scenes, and lets you search the logs with a query language</li></ul><p>This is all you need to work through every exercise below.</p><p><strong>Level 2 — a full NSM platform (a weekend).</strong> When you want the experience of hunting in a SOC-style stack with dashboards and retention:</p><ul><li><a href="https://securityonionsolutions.com/"><strong>Security Onion</strong></a> — free Linux distribution bundling Zeek, Suricata, full packet capture, Elastic stack, and hunt/case-management interfaces. Can import PCAPs for offline analysis or run as a live sensor.</li><li><a href="https://github.com/idaholab/Malcolm"><strong>Malcolm</strong></a> — from Idaho National Laboratory with CISA. Docker-based; ingests PCAP, Zeek and Suricata data into OpenSearch with Arkime for packet search. Particularly strong for ICS/OT protocols.</li><li><a href="https://arkime.com/"><strong>Arkime</strong></a> — indexed full packet capture with session search, great for pivoting from a Zeek finding to the actual packets.</li></ul><p><strong>Level 3 — a live range (ongoing).</strong> A small isolated network with a few Windows and Linux victims, a sensor on a SPAN/virtual switch mirror port, and an “internet” segment where you run your own C2 server. This is where you generate traffic, watch it on the wire, and then hunt it as if you didn’t know it was there. More on this below.</p><p>Whatever you build: <strong>keep malicious PCAPs and lab C2 isolated</strong> from production and from your personal machines. Many datasets contain live malware binaries that can be carved from the traffic.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9w32laEWGY3jjD3jasObjw.png" /><figcaption>Network Threat Hunting Lab Exercise Ideas</figcaption></figure><h3>Set up the Level 1 toolkit</h3><p><strong>Zeek.</strong> The easiest route is the official container image, which avoids dependency headaches:</p><pre># Process a PCAP into Zeek logs in the current directory<br>docker run --rm -v &quot;$(pwd)&quot;:/data -w /data zeek/zeek \<br>  zeek -C -r sample.pcap</pre><pre>ls *.log<br># conn.log  dns.log  http.log  ssl.log  x509.log  files.log ...</pre><p>-C tells Zeek to ignore bad checksums, which are common in captures taken on the sending host because of NIC checksum offloading. Keep the default tab-separated output for these labs — zeek-cut works on TSV. (If you later ship logs to a SIEM, JSON output via LogAscii::use_json=T is usually easier.) For zeek-cut on the host, install Zeek from your distribution or the Zeek package repositories, or run it inside the container.</p><p><strong>tshark.</strong> sudo apt install tshark on Debian/Ubuntu. Handy one-liners for validation:</p><pre># Conversations summary with durations<br>tshark -r sample.pcap -q -z conv,tcp</pre><pre># TLS SNI per client<br>tshark -r sample.pcap -Y tls.handshake.type==1 \<br>  -T fields -e ip.src -e tls.handshake.extensions_server_name | sort | uniq -c | sort -nr</pre><p><strong>RITA.</strong> Current RITA releases run on Docker and are installed with the installer from the <a href="https://github.com/activecm/rita">RITA GitHub releases page</a> (follow the README for your version). Once installed:</p><pre># Import a folder of Zeek logs into a dataset called lab1<br>rita import --database=lab1 --logs=./zeek_logs</pre><pre># Explore beacons, long connections, DNS and threat intel hits<br>rita view lab1</pre><pre># Or dump results to stdout/CSV for scripting<br>rita view --stdout lab1 &gt; lab1.csv</pre><p>RITA ingests Zeek TSV or JSON logs and lets you filter results by severity, IP, beacon score, duration and threat-intel status.</p><h3>Get good practice data</h3><p>You need two kinds of data: traffic known to contain malicious activity, and normal traffic to learn what “boring” looks like.</p><ul><li><a href="https://activecm.github.io/threat-hunting-labs/"><strong>Active Countermeasures Threat Hunting Labs</strong></a> — the lab exercises used throughout this series, with sample data and step-by-step commands.</li><li><a href="https://www.activecountermeasures.com/malware-of-the-day/"><strong>ACM Malware of the Day</strong></a> — 24-hour lab captures of specific C2 frameworks and malware alongside normal Windows traffic, published as PCAPs and as Zeek logs ready for RITA. Perfect for beacon and long-connection practice.</li><li><a href="https://www.malware-traffic-analysis.net/"><strong>malware-traffic-analysis.net</strong></a> — Brad Duncan’s long-running archive of real infection traffic, including training exercises with questions and answers.</li><li><a href="https://www.stratosphereips.org/datasets-overview"><strong>Stratosphere Laboratory datasets</strong></a> — Czech Technical University’s malware, normal and mixed captures, including the well-known CTU-13 botnet dataset.</li><li><a href="https://www.netresec.com/?page=PcapFiles"><strong>Netresec’s list of public PCAP files</strong></a> — an index of CTF, malware, and academic datasets.</li><li><strong>Your own home or lab network</strong> — an overnight capture of your own traffic is the best way to learn how many legitimate beacons exist (updaters, smart TVs, phones) before you hunt somewhere that matters.</li></ul><h3>Five hands-on exercises</h3><p>Each exercise has a goal, the steps, and questions to answer in your notes. Use a 24-hour capture where possible (Malware of the Day is ideal). Resist the urge to read the dataset’s answer key until you have written your own disposition.</p><h4>Exercise 1 — Baseline before you hunt</h4><p><strong>Goal:</strong> understand what normal looks like in the dataset before chasing anything.</p><pre># Which protocols/services are in use?<br>cat conn.log | zeek-cut proto service | sort | uniq -c | sort -nr</pre><pre># Who are the internal hosts and how chatty are they?<br>cat conn.log | zeek-cut id.orig_h | sort | uniq -c | sort -nr | head -20</pre><pre># Top external destinations by number of distinct internal clients<br>cat conn.log | zeek-cut id.orig_h id.resp_h | sort -u \<br>  | cut -f 2 | sort | uniq -c | sort -nr | head -20</pre><p><strong>Questions:</strong> How many internal hosts are there? Which services dominate? Which destinations does <em>every</em> host talk to (likely benign infrastructure) and which ones only one host talks to?</p><h4>Exercise 2 — Find the long connection</h4><p><strong>Goal:</strong> apply Hunt 1 from Part 2.</p><ol><li>List the top 10 single connections by duration (TCP and UDP separately).</li><li>Compute cumulative duration per pair with the awk one-liner from Part 2.</li><li>Compare the two lists. Which pairs appear only in the cumulative list?</li><li>Load the same logs into RITA and compare its long-connection view with your manual results.</li></ol><p><strong>Questions:</strong> Convert the top durations to hours. Which ones can you explain (VPN, chat, sync)? For the ones you cannot, what is the service field, and who owns the destination ASN?</p><h4>Exercise 3 — Prove a beacon by hand, then at scale</h4><p><strong>Goal:</strong> apply Hunt 2. Pick a pair from RITA’s beacon list with a high score, then verify it yourself so you understand what the score means.</p><pre># Replace with your pair<br>SRC=10.55.100.111<br>DST=165.227.216.194</pre><pre># Interval (seconds) between consecutive connections, as a frequency table<br>cat conn.log | zeek-cut ts id.orig_h id.resp_h \<br>  | awk -v s=$SRC -v d=$DST &#39;$2==s &amp;&amp; $3==d {print $1}&#39; | sort -n \<br>  | awk &#39;NR&gt;1 {printf &quot;%d\n&quot;, $1-prev} {prev=$1}&#39; \<br>  | sort -n | uniq -c | sort -nr | head</pre><pre># Bytes sent per connection, as a frequency table<br>cat conn.log | zeek-cut id.orig_h id.resp_h orig_bytes \<br>  | awk -v s=$SRC -v d=$DST &#39;$1==s &amp;&amp; $2==d {print $3}&#39; \<br>  | sort -n | uniq -c | sort -nr | head</pre><p><strong>Questions:</strong> What is the most common interval, and roughly what jitter is applied? How consistent are the sizes? Now take a benign high-scoring beacon (an updater or NTP) and compare: what distinguishes it from the malicious one besides the score?</p><h4>Exercise 4 — Catch a DNS tunnel</h4><p><strong>Goal:</strong> apply Hunt 3.</p><ol><li>Run the unique-subdomain count from Part 2 and list the top 10 base domains.</li><li>Run the query-type distribution. Is TXT, NULL or CNAME unusually high?</li><li>For the top suspicious domain, find the internal source(s) and check conn.log for any direct connections to the resolved IPs.</li><li>Compare with RITA’s DNS view.</li></ol><p><strong>Questions:</strong> How many unique subdomains does the suspicious domain have, and over what time window? What does a typical query label look like (length, character set)? Is the source a client or your internal resolver — and if it is the resolver, which log would you need next?</p><p><em>Stretch goal:</em> in your Level 3 range, run <a href="https://github.com/iagox86/dnscat2">dnscat2</a> or <a href="https://github.com/yarrick/iodine">iodine</a> between two lab VMs for an hour, capture it, and repeat the hunt on traffic you generated.</p><h4>Exercise 5 — A full hunt, start to disposition</h4><p><strong>Goal:</strong> combine everything. Take a <a href="https://www.malware-traffic-analysis.net/">malware-traffic-analysis.net</a> exercise PCAP and run the five-step method end to end.</p><ol><li><strong>Persistence:</strong> long connections and beacons (Exercises 2–3).</li><li><strong>Protocol:</strong> service vs port, rare user-agents in http.log, certificate validation and SNI in ssl.log, JA4 if you have the Zeek package.</li><li><strong>Internal host:</strong> identify it from DHCP (dhcp.log), Kerberos/NTLM logs (hostname, user), and NetBIOS/SMB names.</li><li><strong>Destination:</strong> ASN, domain age, popularity, reputation.</li><li><strong>Score and disposition:</strong> major and minor modifiers, a number, and one of allow-list / collect more / escalate.</li></ol><p>Write it up using the template below, then compare with the site’s answers. Where you were wrong is where you learn the most.</p><h3>Generate your own C2 traffic (Level 3)</h3><p>Public datasets are great, but nothing teaches like hunting traffic you created and know the ground truth of. In an <strong>isolated</strong> range you control:</p><ul><li><strong>Open-source C2 frameworks</strong> — <a href="https://github.com/BishopFox/sliver">Sliver</a> (HTTPS, mTLS, WireGuard and DNS implants) and <a href="https://github.com/its-a-feature/Mythic">Mythic</a> let you configure sleep and jitter, so you can see exactly how a 60s/10% beacon differs from a 3600s/50% one in RITA.</li><li><strong>DNS tunnelling tools</strong> — dnscat2 and iodine for Exercise 4.</li><li><strong>A benign “beacon simulator”</strong> — when you just need regular traffic, a loop like this is enough:</li></ul><pre># Lab only: check in every 60s with ~±15% jitter and a custom user-agent<br>while true; do<br>  curl -s -A &quot;LabUpdater/1.0&quot; http://c2.lab.local/check &gt; /dev/null<br>  sleep $(( 51 + RANDOM % 19 ))<br>done</pre><p>Run the traffic for 24 hours alongside normal user activity (browsing, updates, Teams/Slack), capture it with tcpdump on the mirror port, and hunt it blind a day later. Then change one variable — longer sleep, more jitter, DNS instead of HTTPS, a SaaS-hosted redirector — and see which hunts still catch it.</p><pre># Rotating 24h capture on the sensor: new file every hour, keep 48<br>sudo tcpdump -i eth1 -G 3600 -W 48 -w &#39;lab-%Y%m%d-%H%M.pcap&#39; -n -s 0</pre><h3>Document every hunt</h3><p>A hunt that is not written down cannot be repeated, reviewed, or turned into a detection. A lightweight template, loosely following PEAK’s Prepare → Execute → Act phases:</p><ul><li><strong>Hunt ID and title</strong> — e.g., NTH-2026–014: HTTPS beacons from server VLAN</li><li><strong>Type</strong> — hypothesis / intel-driven / baseline / model-assisted</li><li><strong>Hypothesis</strong> — one testable sentence</li><li><strong>ATT&amp;CK techniques</strong> — e.g., T1071.001, T1573</li><li><strong>Scope</strong> — networks, hosts, time window</li><li><strong>Data sources</strong> — conn.log, dns.log, ssl.log, EDR; confirm they exist and cover the scope</li><li><strong>Queries / analytics</strong> — the exact commands or SIEM queries used</li><li><strong>Findings</strong> — each candidate pair with evidence, score and disposition</li><li><strong>Allow-list changes</strong> — what was added and why</li><li><strong>Detections created or improved</strong></li><li><strong>Visibility gaps found</strong></li><li><strong>Time spent and next steps</strong></li></ul><h3>Turn hunts into detections</h3><p>The end state of a mature program (HMM4 in Bianco’s model) is that each successful hunt becomes an automated detection, freeing the hunters for the next question. Practical ways to get there:</p><ul><li><strong>Schedule RITA.</strong> Import Zeek logs on a schedule (RITA supports rolling databases for continuous import) and alert on new high-score beacons or long connections not on the allow-list.</li><li><strong>SIEM analytics.</strong> Translate the one-liners into scheduled queries: unique subdomains per base domain per hour above a threshold; workstations with a positive PCR to a single destination above N MB; first-seen JA4 fingerprints; outbound connections where port and detected service disagree.</li><li><strong>Zeek scripts and Suricata rules.</strong> Zeek’s scripting language can compute per-host state in real time (for example, counting NXDOMAINs per host). Suricata can catch known C2 profiles, default certificates and JA4/JA3 hashes.</li><li><strong>Feed CTI back.</strong> Infrastructure you confirm (domains, IPs, certificates, JA4 values) goes into your threat-intel platform and blocklists — remembering that the <em>behaviour</em> is the durable detection, not the IOC.</li></ul><h3>Keep learning</h3><p><strong>Courses and certifications</strong></p><ul><li><a href="https://www.sans.org/cyber-security-courses/advanced-network-forensics-threat-hunting-incident-response"><strong>SANS FOR572: Advanced Network Forensics: Threat Hunting, Analysis, and Incident Response</strong></a> — six sections covering packet capture and proxy logs, HTTP and DNS profiling, log aggregation with SOF-ELK, NetFlow, SMB and file-access protocols, Arkime and NetworkMiner, wireless, TLS profiling and protocol reversing, and a capstone that is solved with network evidence only. Includes 20 hands-on labs and maps to the <strong>GIAC Network Forensic Analyst (GNFA)</strong> certification.</li><li><strong>Active Countermeasures</strong> — the <a href="https://www.activecountermeasures.com/wp-content/uploads/2021/02/Network-Threat-Hunting-202102.pdf">Network Threat Hunting training material</a>, free webcasts, blog and Malware of the Day series.</li><li><strong>Hands-on platforms</strong> — CyberDefenders, Blue Team Labs Online and TryHackMe all have network forensics and Zeek/Wireshark challenges at beginner to advanced level.</li></ul><p><strong>Books</strong></p><ul><li>Richard Bejtlich — <em>The Practice of Network Security Monitoring</em> (No Starch Press)</li><li>Chris Sanders and Jason Smith — <em>Applied Network Security Monitoring</em> (Syngress)</li><li>Chris Sanders — <em>Practical Packet Analysis</em> (No Starch Press)</li></ul><p><strong>Free tools and frameworks referenced in this series</strong></p><ul><li><a href="https://zeek.org/">Zeek</a> · <a href="https://github.com/activecm/rita">RITA</a> · <a href="https://www.wireshark.org/">Wireshark/tshark</a> · <a href="https://zui.brimdata.io/">Zui</a> · <a href="https://arkime.com/">Arkime</a></li><li><a href="https://securityonionsolutions.com/">Security Onion</a> · <a href="https://github.com/idaholab/Malcolm">Malcolm</a></li><li><a href="https://github.com/FoxIO-LLC/ja4">JA4+</a> · <a href="https://attack.mitre.org/">MITRE ATT&amp;CK</a> · <a href="https://www.splunk.com/en_us/blog/security/peak-threat-hunting-framework.html">PEAK framework</a></li></ul><h3>Closing thoughts</h3><p>Network threat hunting is not about expensive tools. It is about asking a few hard questions of your traffic, every week, and writing down the answers:</p><ul><li>What is talking out, and does it keep coming back?</li><li>Does the protocol match what it claims to be?</li><li>Which internal host, which process, which user?</li><li>Who is on the other end, and why do we talk to them?</li><li>Benign, uncertain, or compromised?</li></ul><p><strong>You can hide processes, but you can’t hide packets.</strong> Start with a 24-hour capture, a Zeek container and an afternoon. By the end of your tenth hunt you will know your network better than any attacker — and that is the real advantage.</p><p>If this series helped, share it with your SOC team, and tell me in the comments which hunt found something interesting in your environment.</p><h3>References</h3><ul><li>Active Countermeasures — <a href="https://www.activecountermeasures.com/how-to-threat-hunt-your-network/">How to Threat Hunt Your Network</a></li><li>Active Countermeasures — <a href="https://www.activecountermeasures.com/wp-content/uploads/2021/02/Network-Threat-Hunting-202102.pdf">Network Threat Hunting training slides (PDF)</a></li><li>Threat Hunting Labs — <a href="https://activecm.github.io/threat-hunting-labs/long_connections/">Long Connections</a>, <a href="https://activecm.github.io/threat-hunting-labs/beacons/">Beacons</a>, <a href="https://activecm.github.io/threat-hunting-labs/dns/">DNS</a>, <a href="https://activecm.github.io/threat-hunting-labs/outliers/">Outliers</a></li><li>Active Countermeasures — <a href="https://www.activecountermeasures.com/malware-of-the-day/">Malware of the Day</a></li><li>PuppyGraph — <a href="https://www.puppygraph.com/blog/network-threat-hunting">Network Threat Hunting</a></li><li>NetWitness — <a href="https://www.netwitness.com/blog/types-of-network-threat-hunting/">Types of Network Threat Hunting</a></li><li>SANS — <a href="https://www.sans.org/cyber-security-courses/advanced-network-forensics-threat-hunting-incident-response">FOR572</a></li><li>Idaho National Laboratory — <a href="https://github.com/idaholab/Malcolm">Malcolm</a></li></ul><p>Long Live Cyber Defenders!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9aa997edc3e4" width="1" height="1" alt=""><hr><p><a href="https://medium.com/fmisec/hunting-on-the-wire-a-practical-guide-to-network-threat-hunting-part-3-9aa997edc3e4">Hunting on the Wire : A Practical Guide to Network Threat Hunting — Part 3</a> was originally published in <a href="https://medium.com/fmisec">FMI Cyber Security Consulting Services</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Kunai Analysis #1 — Analyzing Linux Activity with Kunai]]></title>
            <link>https://medium.com/fmisec/kunai-analysis-1-analyzing-linux-activity-with-kunai-ba0c4dab7076?source=rss----5aebc5961dd0---4</link>
            <guid isPermaLink="false">https://medium.com/p/ba0c4dab7076</guid>
            <category><![CDATA[blue-team]]></category>
            <category><![CDATA[digital-forensics]]></category>
            <category><![CDATA[linux-threat-hunting]]></category>
            <category><![CDATA[threat-hunting]]></category>
            <category><![CDATA[linux-threat-detection]]></category>
            <dc:creator><![CDATA[Christopher Ryan]]></dc:creator>
            <pubDate>Mon, 02 Nov 2026 02:01:02 GMT</pubDate>
            <atom:updated>2026-11-02T02:01:02.407Z</atom:updated>
            <content:encoded><![CDATA[<p>In the previous article, we looked at how to build Linux threat detection with Kunai on an Ubuntu Server. Detection, however, is only one part of the investigation. The next challenge is understanding what the collected events actually tell us.</p><p>In this blog, we will focus on several important Kunai events and how they can be correlated to understand what happened on a Linux host.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_gv4EoShmP6A3I-vcz8Gvg.jpeg" /></figure><p>For analysis purpose it is more useful to focus on events that help answer these questions:</p><blockquote>What was executed?<br>What files were created or modified?<br>Was a script involved?<br>Did the host communicate with an external system?<br>Was there any persistence activity?<br>Were any artifacts removed?</blockquote><p>For this article, I will focus on six Kunai events that are useful as a starting point for Linux threat analysis:</p><pre>execve<br>execve_script<br>write_config<br>dns_query<br>connect<br>file_unlink</pre><p>The goal is not to analyze these events independently.</p><p>The goal is to correlate them and use the available context to understand what happened on the host.</p><h3>1. Understanding Kunai Events</h3><p>Before looking at individual events, it is useful to understand the structure of a Kunai event. Please find the example event log below</p><pre>{<br> &quot;data&quot;: &quot;…&quot;,<br> &quot;info&quot;: {<br> // Host related information<br> &quot;host&quot;: {<br> // Unique host uuid<br> &quot;uuid&quot;: &quot;c030b40d-0eab-417b-b33a-22d952357984&quot;,<br> // Hostname of the host running kunai<br> &quot;name&quot;: &quot;hal&quot;,<br> // Container information if any, null otherwise<br> &quot;container&quot;: {<br> // Name of the container<br> &quot;name&quot;: &quot;ubuntu-kunai-test&quot;,<br> // Type of container<br> &quot;type&quot;: &quot;docker&quot;<br> }<br> },<br> // Event related information<br> &quot;event&quot;: {<br> // A string which can be used by external tools to identify kunai&#39;s logs<br> &quot;source&quot;: &quot;kunai&quot;,<br> // Event type identifier<br> &quot;id&quot;: 1,<br> // Event type name<br> &quot;name&quot;: &quot;execve&quot;,<br> // Unique identifier for an event<br> &quot;uuid&quot;: &quot;87f5cb64–11ca-fd34–26af-b47f84132543&quot;,<br> // Not relevant, here for diagnosis purposes<br> &quot;batch&quot;: 131<br> },<br> // Information about the task generating this event<br> &quot;task&quot;: {<br> // Task name<br> &quot;name&quot;: &quot;ls&quot;,<br> // Identifier of the task (process or thread)<br> &quot;pid&quot;: 1981502,<br> // Thread Group ID of the task (same for all threads of a process)<br> // When pid == tgid we are in the main thread.<br> &quot;tgid&quot;: 1981502,<br> // A uuid common to all the events generated by the same<br> // task group/process (i.e. all threads). You can search by<br> // this value in order to see the whole activity of a process<br> &quot;guuid&quot;: &quot;cd5415c4–7750–0000–7c85–4ef53e3c1e00&quot;,<br> // Effective uid of the task<br> &quot;uid&quot;: 1000,<br> // Effective gid of the task<br> &quot;gid&quot;: 1000,<br> // Information about Linux namespaces<br> &quot;namespaces&quot;: {<br> // mnt namespace id<br> &quot;mnt&quot;: 4026531841<br> },<br> // task_struct flags https://elixir.bootlin.com/linux/v6.6.5/source/include/linux/sched.h#L767<br> // combination of process flags https://elixir.bootlin.com/linux/v6.6.5/source/include/linux/sched.h#L1726<br> &quot;flags&quot;: &quot;0x400000&quot;,<br> // Indicates whether the task is in a zombie state<br> &quot;zombie&quot;: true<br> },<br> // Information about the parent task. This section<br> // contains the same kind of information as the task section<br> &quot;parent_task&quot;: {<br> &quot;name&quot;: &quot;3&quot;,<br> &quot;pid&quot;: 1981500,<br> &quot;tgid&quot;: 1981500,<br> &quot;guuid&quot;: &quot;a679f5c3–7750–0000–7c85–4ef53c3c1e00&quot;,<br> &quot;uid&quot;: 1000,<br> &quot;gid&quot;: 1000,<br> &quot;namespaces&quot;: {<br> &quot;mnt&quot;: 4026531841<br> },<br> &quot;flags&quot;: &quot;0x400000&quot;,<br> &quot;zombie&quot;: false<br> },<br> // UTC timestamp of the event<br> &quot;utc_time&quot;: &quot;2024–11–19T09:55:53.138247256Z&quot;<br> }<br>}</pre><p>Kunai events generally contain an info section and a data section.</p><p>The info section contains information that can be useful for identifying and correlating events, such as the event name, timestamp, task information, and parent task.</p><p>The data section contains information specific to the event.</p><p>For example, an execution event can contain fields related to the command line, executable, parent process, and process ancestry.</p><p>Some fields that are useful during analysis include:</p><pre>info.event<br>info.task<br>info.parent_task<br>info.utc_time</pre><p>For process-related events, fields such as:</p><pre>data.command_line<br>data.parent_command_line<br>data.parent_exe<br>data.ancestors<br>data.exe.path</pre><p>can provide additional context.</p><h3>2. execve — Start With Process Execution</h3><p>One of the most important events to understand is execve.</p><p>The event is generated when a process executes a program. This makes it one of the most useful starting points when investigating activity on a Linux system.</p><p>For example, consider the following Kunai event:</p><pre>{<br>  &quot;data&quot;: {<br>    &quot;ancestors&quot;: &quot;/usr/lib/systemd/systemd|/usr/bin/login|/usr/bin/zsh|/usr/bin/bash|/usr/bin/xinit|/usr/bin/i3|/usr/bin/bash|/usr/bin/urxvt|/usr/bin/zsh|/usr/bin/bash|/usr/bin/timeout|/usr/bin/bash&quot;,<br>    &quot;parent_command_line&quot;: &quot;bash -ec \&quot;trash=/tmp/trash; mkdir -p $trash; while [ true ]; do t=$(mktemp -d -p $trash); for i in {0..100000}; do echo \\\&quot;AHHHH\\\&quot; &gt; $t/test$i.txt; done &amp;&amp; rm -rf $t; done\&quot;&quot;,<br>    &quot;parent_exe&quot;: &quot;/usr/bin/bash&quot;,<br>    &quot;command_line&quot;: &quot;mktemp -d -p /tmp/trash&quot;,<br>    &quot;exe&quot;: {<br>      &quot;path&quot;: &quot;/usr/bin/mktemp&quot;,<br>      &quot;sha256&quot;: &quot;f32938cf25ddd6f6800a8e9b406595534d0eb27993587bbdeee2e83dd97d8406&quot;<br>    }<br>  },<br>  &quot;info&quot;: {<br>    &quot;event&quot;: {<br>      &quot;name&quot;: &quot;execve&quot;<br>    },<br>    &quot;utc_time&quot;: &quot;2025-06-10T14:00:42.814301638Z&quot;<br>  }<br>}</pre><p>At first glance, there is nothing particularly suspicious about:</p><pre>mktemp -d -p /tmp/trash</pre><p>mktemp is a legitimate Linux utility and is commonly used to create temporary files and directories.</p><p>However, the execve event gives us much more context.</p><p>The parent_command_line shows that the process was launched as part of:</p><pre>bash -ec &quot;trash=/tmp/trash; mkdir -p $trash; while [ true ]; do t=$(mktemp -d -p $trash); ...&quot;</pre><p>This immediately changes the investigation.</p><p>The parent process is repeatedly creating temporary directories under:</p><pre>/tmp/trash</pre><p>and then generating a large number of files:</p><pre>for i in {0..100000}; do<br>    echo &quot;AHHHH&quot; &gt; $t/test$i.txt<br>done</pre><p>The directory is subsequently removed:</p><pre>rm -rf $t</pre><p>So instead of looking at mktemp in isolation, we can understand the behavior from the surrounding process context:</p><pre>bash → mktemp → create temporary directory → create many files → rm -rf</pre><p>This is a good example of why process lineage and command-line context are important during Kunai analysis.</p><p>The executable itself is not necessarily suspicious.</p><p>The <strong>behavior surrounding the executable</strong> is what deserves investigation.</p><h3>What should we check?</h3><p>When reviewing an execve event, I normally start with:</p><pre>command_line<br>parent_command_line<br>parent_exe<br>ancestors<br>exe.path<br>exe.sha256<br>utc_time</pre><p>In this example, command_line tells us:</p><pre>mktemp -d -p /tmp/trash</pre><p>but parent_command_line explains why it was executed.</p><p>The ancestors field provides even more context:</p><pre>systemd<br> → login<br> → zsh<br> → bash<br> → xinit<br> → i3<br> → bash<br> → urxvt<br> → zsh<br> → bash<br> → timeout<br> → bash<br> → mktemp</pre><p>This does not automatically make the activity malicious. Instead, it gives the analyst enough context to continue the investigation.</p><p>For example, we could now look for the subsequent file creation and deletion activity associated with the temporary directory.</p><p>This is the important lesson when analyzing execve:</p><blockquote><strong><em>Do not judge a process only by its executable name. Look at what executed it, how it was executed, and what happened around it.</em></strong></blockquote><h3>3. execve_script — Investigate Script Execution</h3><p>The next event worth looking at is execve_script.</p><p>This event is useful when a process executes a script through an interpreter such as Bash, Python, or another scripting engine.</p><p>Consider this Kunai event:</p><pre>{<br>  &quot;data&quot;: {<br>    &quot;parent_command_line&quot;: &quot;/bin/bash generate-activity.sh&quot;,<br>    &quot;parent_exe&quot;: &quot;/usr/bin/bash&quot;,<br>    &quot;command_line&quot;: &quot;/bin/bash /tmp/tmp.KsA9Ym95fq_kunai_test.sh&quot;,<br>    &quot;exe&quot;: {<br>      &quot;path&quot;: &quot;/tmp/tmp.KsA9Ym95fq_kunai_test.sh&quot;,<br>      &quot;sha256&quot;: &quot;cda81b42b75647daf6b70a626380c199fe665d721e63bfe34c96b65da0289627&quot;,<br>      &quot;size&quot;: 21<br>    },<br>    &quot;interpreter&quot;: {<br>      &quot;path&quot;: &quot;/usr/bin/bash&quot;,<br>      &quot;sha256&quot;: &quot;68bac453a428796f18114a1bf43420d814e36555808ec98eacb782de565c06eb&quot;,<br>      &quot;size&quot;: 1100632<br>    }<br>  },<br>  &quot;info&quot;: {<br>    &quot;event&quot;: {<br>      &quot;name&quot;: &quot;execve_script&quot;<br>    },<br>    &quot;utc_time&quot;: &quot;2025-06-10T14:00:30.925394727Z&quot;<br>  }<br>}</pre><p>The first thing we can see is:</p><pre>/bin/bash /tmp/tmp.KsA9Ym95fq_kunai_test.sh</pre><p>This tells us that Bash executed a script located under /tmp.</p><p>The event also provides a separate interpreter object:</p><pre>interpreter.path:<br>/usr/bin/bash</pre><p>This is useful because we can distinguish between the <strong>script being executed</strong> and the <strong>program responsible for interpreting it</strong>.</p><p>In this case:</p><pre>Script      → /tmp/tmp.KsA9Ym95fq_kunai_test.sh<br>Interpreter → /usr/bin/bash</pre><p>We can also see how the script was launched:</p><pre>parent_command_line:<br>/bin/bash generate-activity.sh</pre><p>So the immediate activity can be summarized as:</p><pre>generate-activity.sh → /tmp/tmp.KsA9Ym95fq_kunai_test.sh</pre><p>This is already useful during an investigation.</p><p>Instead of simply seeing:</p><pre>bash</pre><p>we know that Bash was used to execute a specific script from /tmp.</p><h3>Why does the script location matter?</h3><p>The location does not automatically make the script malicious.</p><p>/tmp is commonly used by legitimate applications and scripts.</p><p>However, a script executed from a temporary directory is worth investigating when it appears together with other suspicious activity.</p><p>For example:</p><pre>execve → file creation → execve_script</pre><p>could indicate that a process created a temporary script and subsequently executed it.</p><p>If we then observe network activity:</p><pre>execve → file creation → execve_script → connect</pre><p>we now have a stronger reason to investigate the script and the process responsible for it.</p><p>The important point is that execve_script gives us additional context that a normal process execution event may not provide.</p><h3>What should we check?</h3><p>When reviewing execve_script, focus on:</p><pre>command_line<br>parent_command_line<br>parent_exe<br>exe.path<br>exe.sha256<br>interpreter.path<br>interpreter.sha256<br>utc_time</pre><p>For example:</p><pre>Script:<br> /tmp/tmp.KsA9Ym95fq_kunai_test.sh</pre><pre>Interpreter:<br> /usr/bin/bash</pre><pre>Parent:<br> /bin/bash generate-activity.sh</pre><p>This gives us a simple investigation starting point:</p><blockquote><strong><em>What is the script, who executed it, and why was it executed?</em></strong></blockquote><h3>A useful correlation</h3><p>If the script was created shortly before the execve_script event, we can investigate the file creation event.</p><p>For example:</p><pre>file_create → execve_script</pre><p>If the script then makes an external connection:</p><pre>file_create → execve_script → dns_query → connect</pre><p>we can investigate the entire activity rather than treating the script execution as an isolated event.</p><p>This is where Kunai becomes useful for analysis: the event tells us what happened, while correlation helps us understand why it happened.</p><h3>4. write_config — Investigate Configuration Changes</h3><p>Configuration files are important evidence during an investigation because attackers may modify them to change how a service or application behaves.</p><p>The write_config event helps identify when a process writes to a configuration file.</p><p>For example, this event was generated when tee wrote to /etc/kunai_test.conf:</p><pre>{<br>  &quot;data&quot;: {<br>    &quot;ancestors&quot;: &quot;/usr/lib/systemd/systemd|/usr/bin/login|/usr/bin/zsh|/usr/bin/bash|/usr/bin/xinit|/usr/bin/i3|/usr/bin/bash|/usr/bin/urxvt|/usr/bin/zsh|/usr/bin/bash|/usr/bin/sudo|/usr/bin/sudo&quot;,<br>    &quot;command_line&quot;: &quot;tee /etc/kunai_test.conf&quot;,<br>    &quot;exe&quot;: {<br>      &quot;path&quot;: &quot;/usr/bin/tee&quot;<br>    },<br>    &quot;path&quot;: &quot;/etc/kunai_test.conf&quot;<br>  },<br>  &quot;info&quot;: {<br>    &quot;host&quot;: &quot;...&quot;,<br>    &quot;event&quot;: {<br>      &quot;source&quot;: &quot;kunai&quot;,<br>      &quot;id&quot;: 84,<br>      &quot;name&quot;: &quot;write_config&quot;,<br>      &quot;uuid&quot;: &quot;4903ced0-5bad-c3db-9325-5ea6a6c4da90&quot;,<br>      &quot;batch&quot;: 877<br>    },<br>    &quot;task&quot;: &quot;...&quot;,<br>    &quot;parent_task&quot;: &quot;...&quot;,<br>    &quot;utc_time&quot;: &quot;2024-10-29T12:48:02.474792288Z&quot;<br>  }<br>}</pre><p>The important fields here are:</p><ul><li>command_line — shows how the configuration was written.</li><li>exe.path — shows the program performing the write, in this case /usr/bin/tee.</li><li>path — shows the configuration file that was modified.</li><li>ancestors — provides the process history leading to the activity.</li><li>utc_time — tells us when the modification occurred.</li></ul><p>The key detail is:</p><pre>tee /etc/kunai_test.conf</pre><p>This tells us that /usr/bin/tee was used to write to /etc/kunai_test.conf.</p><p>The process ancestry also shows that the activity came through sudo:</p><pre>... → bash → sudo → sudo → tee</pre><p>This is useful because a configuration change should not be analyzed only from the file path. We also want to understand <strong>who initiated the change and how the process obtained the privileges required to modify the file</strong>.</p><p>A configuration write is not automatically malicious. System administrators and applications routinely modify files under /etc. The investigation becomes more interesting when the modified file, process ancestry, command line, or timing is unusual.</p><p>For example, an analyst could correlate the event with other activity:</p><pre>execve → write_config → execve_script</pre><p>This could indicate that a process was executed, a configuration file was modified, and a script was subsequently executed.</p><p>The main question when analyzing write_config is:</p><blockquote><strong><em>What configuration was changed, who changed it, and why?</em></strong></blockquote><p>This makes write_config particularly useful for finding changes that may support persistence, alter application behavior, or modify system configuration.</p><h3>5. dns_query — Investigate DNS Activity</h3><p>DNS activity is useful during an investigation because it can show which domains a process tried to resolve before making a network connection.</p><p>Kunai’s dns_query event provides information about the DNS query, the response, and the DNS resolver that handled the request. The event is generated for DNS traffic over UDP/53 or TCP/53.</p><p>For example, this event was generated when curl accessed a Kunai-related website:</p><pre>{<br>  &quot;data&quot;: {<br>    &quot;ancestors&quot;: &quot;/usr/lib/systemd/systemd|/usr/bin/login|/usr/bin/zsh|/usr/bin/bash|/usr/bin/xinit|/usr/bin/i3|/usr/bin/bash|/usr/bin/urxvt|/usr/bin/zsh|/usr/bin/bash&quot;,<br>    &quot;command_line&quot;: &quot;curl https://why.kunai.rocks&quot;,<br>    &quot;exe&quot;: {<br>      &quot;path&quot;: &quot;/usr/bin/curl&quot;<br>    },<br>    &quot;socket&quot;: {<br>      &quot;domain&quot;: &quot;AF_INET&quot;,<br>      &quot;type&quot;: &quot;SOCK_DGRAM&quot;,<br>      &quot;proto&quot;: &quot;UDP&quot;<br>    },<br>    &quot;src&quot;: {<br>      &quot;ip&quot;: &quot;192.168.1.152&quot;,<br>      &quot;port&quot;: 13267<br>    },<br>    &quot;query&quot;: &quot;kunai-project.github.io&quot;,<br>    &quot;response&quot;: &quot;185.199.110.153;185.199.111.153;185.199.109.153;185.199.108.153&quot;,<br>    &quot;dns_server&quot;: {<br>      &quot;ip&quot;: &quot;192.168.1.1&quot;,<br>      &quot;port&quot;: 53,<br>      &quot;public&quot;: false,<br>      &quot;is_v6&quot;: false<br>    },<br>    &quot;community_id&quot;: &quot;1:XT9OMvFiajrWmMOXr4N3TI+3Etc=&quot;<br>  },<br>  &quot;info&quot;: {<br>    &quot;host&quot;: &quot;...&quot;,<br>    &quot;event&quot;: {<br>      &quot;source&quot;: &quot;kunai&quot;,<br>      &quot;id&quot;: 61,<br>      &quot;name&quot;: &quot;dns_query&quot;,<br>      &quot;uuid&quot;: &quot;ecc87c39-8da3-0b9d-4060-480fdef2efd7&quot;,<br>      &quot;batch&quot;: 878<br>    },<br>    &quot;task&quot;: &quot;...&quot;,<br>    &quot;parent_task&quot;: &quot;...&quot;,<br>    &quot;utc_time&quot;: &quot;2024-10-29T12:48:02.470834988Z&quot;<br>  }<br>}</pre><p>The first thing to look at is the process that generated the DNS activity:</p><pre>command_line: curl https://why.kunai.rocks<br>exe.path: /usr/bin/curl</pre><p>This gives us context about <strong>which program initiated the activity and what it was trying to access</strong>.</p><p>The actual DNS lookup is shown by:</p><pre>query: kunai-project.github.io</pre><p>Kunai also records the returned IP addresses:</p><pre>response: 185.199.110.153<br>         185.199.111.153<br>         185.199.109.153<br>         185.199.108.153</pre><p>This is useful because the domain alone does not tell the complete story. During an investigation, we may also want to determine which IP addresses the host received and whether those addresses were subsequently contacted.</p><p>The DNS resolver is also recorded:</p><pre>dns_server:<br>  ip: 192.168.1.1<br>  port: 53<br>  public: false</pre><p>Here, the host sent the DNS request to the internal resolver 192.168.1.1 over UDP/53.</p><p>The important fields to inspect are:</p><ul><li>command_line — what the process was doing.</li><li>exe.path — which executable generated the activity.</li><li>query — the domain being resolved.</li><li>response — IP addresses returned by DNS.</li><li>dns_server — the DNS resolver used by the host.</li><li>utc_time — when the lookup occurred.</li><li>ancestors — how the process was launched.</li></ul><p>From a DFIR perspective, the value of dns_query becomes much greater when correlated with process and network events.</p><p>For example:</p><pre>execve → dns_query → connect</pre><p>This can give us a simple investigation path:</p><p><strong>A process was executed → it resolved a domain → it established a network connection.</strong></p><p>If the domain is suspicious, we can then go back to the process that generated the lookup and investigate its command line, parent process, executable path, and file activity.</p><p>The main question when analyzing dns_query is:</p><blockquote><strong><em>Which process resolved the domain, what did the domain resolve to, and what happened next?</em></strong></blockquote><p>This shifts DNS analysis from simply looking at domains to reconstructing the activity that happened around them.</p><h3>6. connect — Identify Outbound Connections</h3><p>After identifying DNS activity, the next question is: <strong>did the process actually connect to the resolved IP address?</strong></p><p>The connect event is generated when a process attempts to connect to a remote IP address. Kunai can also correlate the destination hostname with a previous dns_query event, making it easier to connect the domain name with the actual network connection.</p><p>For example, this event was generated by curl:</p><pre>{<br>  &quot;data&quot;: {<br>    &quot;ancestors&quot;: &quot;/usr/lib/systemd/systemd|/usr/bin/login|/usr/bin/zsh|/usr/bin/bash|/usr/bin/xinit|/usr/bin/i3|/usr/bin/bash|/usr/bin/urxvt|/usr/bin/zsh|/usr/bin/bash&quot;,<br>    &quot;command_line&quot;: &quot;curl -d RCKhLa0Ka70LTYgF4eJiQkAspo9Yevj3dMjuSc0NwpOauPicV3P4bajH1paLQMvYGIILwrHk3cpxgS4FDVAUn5XiT0ucGNoVYd2FAjg6hP1zXSH9KCKJhB3ixUq8APtqSJ1DPVYGPOSyCIaHb9Nh4QOJe0UiYjogNoiFBRYVIL8hFgI607LW0MVxcTQqPdX9Utuw9MXxyEaN6TVcFWmKAxrV5BsgP9YYeXyWwda62Kg5aqsq2cdPy9jm9s6S6Brr https://why.kunai.rocks&quot;,<br>    &quot;exe&quot;: {<br>      &quot;path&quot;: &quot;/usr/bin/curl&quot;<br>    },<br>    &quot;socket&quot;: {<br>      &quot;domain&quot;: &quot;AF_INET&quot;,<br>      &quot;type&quot;: &quot;SOCK_DGRAM&quot;,<br>      &quot;proto&quot;: &quot;UDP&quot;<br>    },<br>    &quot;src&quot;: {<br>      &quot;ip&quot;: &quot;192.168.1.152&quot;,<br>      &quot;port&quot;: 36749<br>    },<br>    &quot;dst&quot;: {<br>      &quot;hostname&quot;: &quot;kunai-project.github.io&quot;,<br>      &quot;ip&quot;: &quot;185.199.111.153&quot;,<br>      &quot;port&quot;: 443,<br>      &quot;public&quot;: true,<br>      &quot;is_v6&quot;: false<br>    },<br>    &quot;community_id&quot;: &quot;1:Zy0KM8lSsDjFs4mzmfFHjtBZfPI=&quot;,<br>    &quot;connected&quot;: true<br>  },<br>  &quot;info&quot;: {<br>    &quot;host&quot;: &quot;...&quot;,<br>    &quot;event&quot;: {<br>      &quot;source&quot;: &quot;kunai&quot;,<br>      &quot;id&quot;: 60,<br>      &quot;name&quot;: &quot;connect&quot;,<br>      &quot;uuid&quot;: &quot;6c1ab8dc-3e55-cdc9-12ea-b14d15366dba&quot;,<br>      &quot;batch&quot;: 877<br>    },<br>    &quot;task&quot;: &quot;...&quot;,<br>    &quot;parent_task&quot;: &quot;...&quot;,<br>    &quot;utc_time&quot;: &quot;2024-10-29T12:48:02.472813816Z&quot;<br>  }<br>}</pre><p>The first thing to identify is the process responsible for the connection:</p><pre>command_line: curl ... https://why.kunai.rocks<br>exe.path: /usr/bin/curl</pre><p>This tells us that /usr/bin/curl attempted the network connection.</p><p>The destination is shown under dst:</p><pre>hostname: kunai-project.github.io<br>ip:       185.199.111.153<br>port:     443<br>public:   true</pre><p>This gives us the complete destination context:</p><p><strong>curl → </strong><strong>kunai-project.github.io → </strong><strong>185.199.111.153:443</strong></p><p>The connected field is also important:</p><pre>connected: true</pre><p>In this event, the connection attempt was successful.</p><p>This is where connect becomes particularly useful when combined with the previous dns_query event. The earlier DNS event showed that kunai-project.github.io resolved to several IP addresses, including 185.199.111.153. The connect event then shows that the host actually connected to that IP.</p><p>We can therefore reconstruct a simple network activity chain:</p><pre>dns_query → connect</pre><p>Or, when combined with process execution:</p><pre>execve → dns_query → connect</pre><p>This gives the analyst more than just a list of network connections. We can start asking:</p><ul><li>Which process made the connection?</li><li>What domain was involved?</li><li>Which IP address was contacted?</li><li>Which port was used?</li><li>Was the destination public or internal?</li><li>Did the connection succeed?</li><li>What happened immediately before and after the connection?</li></ul><p>The command_line is especially important in this example. It contains a large -d argument before the destination URL. That does not automatically make the activity malicious, but it gives the analyst something to investigate further because the process was sending data as part of the request.</p><p>The main fields to inspect are:</p><ul><li>command_line — what the process was doing.</li><li>exe.path — which executable made the connection.</li><li>dst.hostname — the destination hostname, when available.</li><li>dst.ip — the remote IP address.</li><li>dst.port — the destination port.</li><li>dst.public — whether the destination is identified as public.</li><li>connected — whether the connection succeeded.</li><li>utc_time — when the connection occurred.</li><li>ancestors — the process ancestry.</li></ul><p>From a DFIR perspective, connect helps answer the next question after DNS analysis:</p><blockquote><strong><em>After resolving the domain, did the process actually communicate with the destination?</em></strong></blockquote><p>This makes dns_query and connect especially useful together. One shows the <strong>name resolution</strong>, while the other shows the <strong>network connection attempt</strong>.</p><p>The investigation can then continue by correlating the connection with file activity, scripts, or other processes to understand what the network communication was part of.</p><h3>7. file_unlink — Investigate File Deletion</h3><p>The file_unlink event is generated when a file is deleted from the filesystem.</p><p>File deletion can be completely normal. Linux services routinely create and remove temporary files, sockets, runtime files, and other artifacts. However, attackers may also delete files to remove traces after executing malicious activity.</p><p>For example, this event shows a file being removed from /run/udev:</p><pre>{<br>  &quot;data&quot;: {<br>    &quot;ancestors&quot;: &quot;/usr/lib/systemd/systemd|/usr/bin/udevadm&quot;,<br>    &quot;command_line&quot;: &quot;/usr/lib/systemd/systemd-udevd&quot;,<br>    &quot;exe&quot;: {<br>      &quot;path&quot;: &quot;/usr/bin/udevadm&quot;<br>    },<br>    &quot;path&quot;: &quot;/run/udev/watch/b43:128&quot;,<br>    &quot;success&quot;: true<br>  },<br>  &quot;info&quot;: {<br>    &quot;host&quot;: &quot;...&quot;,<br>    &quot;event&quot;: {<br>      &quot;source&quot;: &quot;kunai&quot;,<br>      &quot;id&quot;: 86,<br>      &quot;name&quot;: &quot;file_unlink&quot;,<br>      &quot;uuid&quot;: &quot;66d58529-0b24-ef1d-2b27-3e5a5a85d31b&quot;,<br>      &quot;batch&quot;: 643<br>    },<br>    &quot;task&quot;: &quot;...&quot;,<br>    &quot;parent_task&quot;: &quot;...&quot;,<br>    &quot;utc_time&quot;: &quot;2024-10-29T12:47:59.189301629Z&quot;<br>  }<br>}</pre><p>The most important field is:</p><pre>path: /run/udev/watch/b43:128</pre><p>This tells us which file was removed.</p><p>We can then look at the process responsible:</p><pre>command_line: /usr/lib/systemd/systemd-udevd<br>exe.path:    /usr/bin/udevadm</pre><p>The process ancestry also gives us additional context:</p><pre>systemd → udevadm</pre><p>In this particular example, the deleted file is under /run/udev, and the activity is associated with the system&#39;s udev components. This can be consistent with normal system activity rather than malicious file deletion.</p><p>The success field tells us whether the unlink operation succeeded:</p><pre>success: true</pre><p>This means the file was successfully removed.</p><p>The important fields to inspect are:</p><ul><li>path — which file was deleted.</li><li>command_line — what the process was doing.</li><li>exe.path — which executable performed the deletion.</li><li>ancestors — how the process was launched.</li><li>success — whether the deletion succeeded.</li><li>utc_time — when the deletion occurred.</li></ul><p>The key point is that <strong>a file deletion event by itself does not prove malicious activity</strong>.</p><p>Instead, look at the surrounding events. For example, if an unusual script creates a file, executes it, communicates externally, and then deletes it, the deletion becomes much more interesting:</p><pre>file_create → execve_script → dns_query → connect → file_unlink</pre><p>This sequence could represent a temporary file being created, executed, used for network communication, and then removed.</p><p>In contrast, a deletion involving a normal system process and a temporary runtime file may simply be routine Linux activity.</p><p>The main question when analyzing file_unlink is:</p><blockquote><strong><em>What was deleted, which process deleted it, and does the deletion make sense in the surrounding activity?</em></strong></blockquote><p>This makes file_unlink particularly useful when investigating <strong>cleanup behavior</strong>. Rather than treating every deletion as suspicious, use it as another piece of evidence when reconstructing what happened on the host.</p><h3>8. Putting the Events Together</h3><p>The real value of Kunai analysis comes from correlation.</p><p>For example, imagine we observe:</p><pre>execve → file_create → execve_script</pre><p>This tells us that a process executed a command, a file was created, and a script was subsequently executed.</p><p>We can then continue the investigation:</p><pre>execve → file_create → execve_script → dns_query → connect</pre><p>Now we have execution, file activity, script execution, and network activity occurring in the same investigation window.</p><p>If the activity ends with:</p><pre>file_unlink</pre><p>we may have:</p><pre>execve → file_create → execve_script → dns_query → connect → file_unlink</pre><p>This is much more useful than reviewing six unrelated JSON events.</p><p>The analyst can now investigate the sequence as a single activity:</p><blockquote><em>What executed the initial command?</em></blockquote><blockquote><em>What file was created?</em></blockquote><blockquote><em>What script was executed?</em></blockquote><blockquote><em>What domain was queried?</em></blockquote><blockquote><em>What remote system was contacted?</em></blockquote><blockquote><em>What file was eventually deleted?</em></blockquote><p>This is the basic approach I would recommend when starting Kunai analysis.</p><h3>9. A Practical Analysis Workflow</h3><p>When starting with a Kunai log, I would begin with execve.</p><p>Look for unusual commands, unexpected binaries, suspicious locations, and unusual parent-child relationships.</p><p>From there, check execve_script if scripts or interpreters are involved.</p><p>If the activity creates or modifies files, investigate the corresponding file_create, write, or write_config events.</p><p>For network-related activity, correlate dns_query with connect to determine what the process was attempting to reach.</p><p>Finally, check file_unlink to determine whether any artifacts were removed after execution.</p><p>A simple investigation may therefore look like:</p><pre>execve → file_create → execve_script → write_config → dns_query → connect → file_unlink</pre><p>Not every investigation will contain all of these events.</p><p>The purpose is to use whichever events are available to build the most complete timeline possible.</p><h3>10. What Should the Analyst Produce?</h3><p>The final result of the investigation should not simply be:</p><pre>Suspicious execve detected.<br>Suspicious connect detected.<br>Suspicious file deletion detected.</pre><p>That does not explain what happened.</p><p>Instead, the analyst should be able to describe the activity in context.</p><p>For example:</p><blockquote><em>A shell process executed a command that resulted in a script being created and executed from </em><em>/tmp. The script subsequently generated DNS activity and established an outbound connection to a remote system. The temporary script was later deleted.</em></blockquote><h3>Conclusion</h3><p>Kunai provides a large amount of telemetry that can be used to investigate Linux activity. However, analysts do not need to start by reviewing every event.</p><p>For a basic investigation, six events provide a useful starting point:</p><pre>execve<br>execve_script<br>write_config<br>dns_query<br>connect<br>file_unlink</pre><p>Each event provides a different piece of the investigation.</p><p>execve helps us understand process execution.</p><p>execve_script provides additional visibility into script execution and interpreters.</p><p>write_config helps identify configuration changes.</p><p>dns_query and connect provide network context.</p><p>file_unlink can help identify cleanup activity.</p><p>The important part is how these events are correlated.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ba0c4dab7076" width="1" height="1" alt=""><hr><p><a href="https://medium.com/fmisec/kunai-analysis-1-analyzing-linux-activity-with-kunai-ba0c4dab7076">Kunai Analysis #1 — Analyzing Linux Activity with Kunai</a> was originally published in <a href="https://medium.com/fmisec">FMI Cyber Security Consulting Services</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Hunting on the Wire : A Practical Guide to Network Threat Hunting — Part 2]]></title>
            <link>https://medium.com/fmisec/hunting-on-the-wire-a-practical-guide-to-network-threat-hunting-part-2-74c73587eb5e?source=rss----5aebc5961dd0---4</link>
            <guid isPermaLink="false">https://medium.com/p/74c73587eb5e</guid>
            <category><![CDATA[blue-team]]></category>
            <category><![CDATA[network-threat-hunting]]></category>
            <category><![CDATA[network-forensics]]></category>
            <category><![CDATA[threat-hunting]]></category>
            <category><![CDATA[zeek]]></category>
            <dc:creator><![CDATA[Digit Oktavianto]]></dc:creator>
            <pubDate>Wed, 28 Oct 2026 02:01:04 GMT</pubDate>
            <atom:updated>2026-10-28T02:01:04.495Z</atom:updated>
            <content:encoded><![CDATA[<p><em>A technical walkthrough of the four core network hunts — what to look for, why it works, the exact commands, and how to decide if it’s malicious.</em></p><p><em>This is Part 2 of a three-part series. Part 1 covered why the network is the hunter’s ground truth. Part 3 is about building a lab and practising. This part is the technical core.</em></p><p>In Part 1 we settled on a simple principle: <strong>start with persistence</strong>. An implant that cannot talk to its operator is useless, so the strongest evidence of compromise is a communication channel that keeps coming back. In this part we turn that principle into four concrete hunts you can run against Zeek logs or PCAP today.</p><p>All examples use <a href="https://zeek.org/">Zeek</a> logs and the open-source tools from Active Countermeasures’ <a href="https://activecm.github.io/threat-hunting-labs/">Threat Hunting Labs</a>. Zeek logs are tab-separated, and zeek-cut lets you pick columns by name, which makes them perfect for Unix one-liners.</p><h3>The method: five steps, highest threat first</h3><p>ACM’s <a href="https://www.activecountermeasures.com/how-to-threat-hunt-your-network/">How to Threat Hunt Your Network</a> gives an ordered workflow. Every hunt in this article plugs into it:</p><ol><li><strong>Find persistent channels</strong> leaving the network (long connections and beacons).</li><li><strong>Analyse the protocol.</strong> Does the application match the port? Is the beaconing normal for this protocol (NTP beacons by design)? Unique user-agent or TLS fingerprint? Self-signed certificate?</li><li><strong>Evaluate the internal host.</strong> What is it, who uses it, what process made the connection?</li><li><strong>Check the destination.</strong> Who owns the IP/ASN? How old is the domain? Is it a known business partner?</li><li><strong>Disposition.</strong> Allow-list with a documented reason, collect more data, or escalate to IR.</li></ol><p>A useful habit: work on <strong>internal-to-external IP pairs</strong>, not individual connections. A single connection tells you little. A pair that has talked 2,000 times over 24 hours at near-identical intervals tells you a lot.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/507/1*GuO7S3kxC5jwFrAmUk-N9w.png" /><figcaption>Zeek NIDS for Network Threat Hunting</figcaption></figure><h3>Hunt 1: Long connections</h3><p><strong>The idea.</strong> Some C2 frameworks hold a single session open and push commands down it. Reverse shells, SSH tunnels, some web-socket implants and many RATs behave this way. ACM’s training suggests treating outbound connections that last <strong>more than 12–24 hours</strong> as worth an explanation.</p><p><strong>A quick note on what a “connection” is.</strong> TCP connections have a clear start (three-way handshake) and end (FIN/RST). UDP has no session, so tools invent one: most close a UDP “connection” after <strong>30–60 seconds</strong> of silence. Zeek identifies each connection by its 5-tuple (source IP, source port, destination IP, destination port, protocol). That matters because a long UDP flow in Zeek means the two hosts never stopped talking for more than that idle window.</p><p><strong>Step 1 — the longest single connections.</strong> From the <a href="https://activecm.github.io/threat-hunting-labs/long_connections/">long connections lab</a>:</p><pre>cat conn.log | zeek-cut id.orig_h id.orig_p id.resp_h id.resp_p proto service duration \<br>  | sort -nrk 7 | head</pre><p>Column 7 is duration in seconds. Divide by 3600 to get hours. Repeat with grep udp to look at UDP specifically — long UDP sessions to the internet that are not DNS, NTP or a known VPN are unusual.</p><p><strong>Step 2 — cumulative time per pair.</strong> Smarter malware drops and re-opens the connection to stay under a single-session threshold. So add up the duration for each source/destination/port/protocol tuple:</p><pre>cat conn.log | zeek-cut id.orig_h id.resp_h id.resp_p proto duration \<br>  | awk &#39;BEGIN{ FS=&quot;\t&quot; } { arr[$1 FS $2 FS $3 FS $4] += $5 } \<br>    END{ for (key in arr) printf &quot;%s%s%s\n&quot;, key, FS, arr[key] }&#39; \<br>  | sort -nrk 5 | head</pre><p>A pair with 86,000 cumulative seconds in a 24-hour capture was connected for essentially the whole day, even if no individual session was long.</p><p><strong>Step 3 — let RITA do it at scale.</strong> <a href="https://github.com/activecm/rita">RITA</a> (Real Intelligence Threat Analytics) imports Zeek logs and computes this for every pair. In the lab’s older RITA version the command was rita show-long-connections -H --limit 10 &lt;dataset&gt;; current RITA releases show long connections inside the rita view interface, sortable by duration.</p><p><strong>What’s usually benign.</strong> Expect to allow-list a handful of things: BGP sessions, site-to-site VPNs, chat and collaboration clients (Teams, Slack), cloud storage sync, remote-management agents, push-notification services and long-polling web apps. Record the business reason when you allow-list — “it’s Microsoft” is not a reason.</p><p><strong>What’s suspicious.</strong> A workstation or server holding a session for many hours to a VPS or residential IP with no business relationship. A long session on 443 where Zeek’s service column is empty (not TLS). A long SSH session outbound from a host that has no reason to SSH anywhere. Long connections to a cloud IP that is fronting a domain registered last week.</p><h3>Hunt 2: Beacons</h3><p><strong>The idea.</strong> Most modern C2 does not hold a session open. The implant sleeps, wakes up, opens a short connection to ask for tasks, and goes back to sleep. Cobalt Strike, Sliver, Mythic, Havoc, Brute Ratel and nearly every commodity RAT work this way. The defining property is <strong>regularity</strong>: the check-ins happen on a schedule, and the check-in packets look alike.</p><p>Attackers know this, so they add <strong>jitter</strong> — a random percentage applied to the sleep time. A 60-second sleep with 20% jitter produces intervals anywhere between 48 and 72 seconds. Jitter makes beacons harder to see by eye, but it does not make them random. Over hours, the intervals still cluster tightly around a centre, and the request and response sizes stay consistent because the “no tasks” response is always the same.</p><p><strong>What to measure for each internal→external pair:</strong></p><ul><li><strong>Connection count</strong> over the window — beacons generate many connections (60-second sleep = ~1,440 per day).</li><li><strong>Interval regularity</strong> — the time delta between consecutive connections. Look at the mode (most common interval), the spread (range, median absolute deviation) and the skew (is the distribution symmetric around the centre?).</li><li><strong>Size regularity</strong> — bytes per connection. An idle beacon sends and receives nearly identical payloads every time.</li><li><strong>Duration of the pattern</strong> — a pattern that persists across the whole 24 hours, including nights and weekends when users are not active, is much more suspicious than a burst.</li></ul><p>ACM’s guidance is to analyse <strong>at least 24 hours</strong> of data. Short windows let jitter dominate. Longer windows make the statistical centre obvious, and they expose “low and slow” beacons that check in only every few hours.</p><p><strong>Doing it by hand with Wireshark / tshark.</strong> The <a href="https://activecm.github.io/threat-hunting-labs/beacons/">beacons lab</a> shows the manual approach. Filter one pair in Wireshark and add a column for frame.time_delta_displayed — the time since the previous displayed packet. Or do the same with tshark:</p><pre>tshark -r sample.pcap -T fields -e ip.src -e ip.dst -e udp.dstport \<br>  -e frame.time_delta_displayed \<br>  &#39;ip.src==192.168.88.2 &amp;&amp; ip.dst==165.227.88.15&#39; | head -25</pre><p>In the lab dataset the packets arrive almost exactly <strong>one second apart</strong>. Export interval and packet size to CSV and plot them as histograms:</p><pre>tshark -r sample.pcap -T fields -E separator=, -e ip.len \<br>  -e frame.time_delta_displayed \<br>  &#39;ip.src==192.168.88.2 &amp;&amp; ip.dst==165.227.88.15&#39; &gt; sample.csv</pre><p>A human browsing produces a long-tailed, messy histogram. A beacon produces one tall spike (or a narrow hump if jittered) for both interval and size.</p><p><strong>Doing it at scale with RITA.</strong> By hand you can check one pair; a network has tens of thousands. RITA scores every pair automatically, from <strong>0 to 1</strong>, combining several statistics. The lab explains the key fields:</p><ul><li><strong>Score</strong> — overall likelihood of beaconing; the closer to 1, the stronger.</li><li><strong>Interval skew / size skew</strong> — near 0 means the distribution is symmetric, as you would expect from a timer with jitter.</li><li><strong>Dispersion (MAD) Median Absolute Deviation</strong> — near 0 means intervals or sizes cluster tightly around the median.</li><li><strong>Connection count and mode</strong> — how many check-ins, and the most common interval and size.</li></ul><p>The classic lab command was rita show-beacons &lt;dataset&gt;. In current releases you run rita import on your Zeek logs and browse the beacon results in rita view, sorted by score. Treat anything <strong>above roughly 0.8</strong> as a priority, and anything above 0.9 that is not a known updater as a likely finding.</p><p><strong>What’s usually benign.</strong> NTP (it beacons by design), Windows Update and other software updaters, AV/EDR cloud check-ins, telemetry agents, monitoring (SNMP polls, health checks), mail clients polling IMAP, and some ad or analytics scripts in browser tabs left open overnight. You will allow-list these on your first few hunts and then the list stabilises.</p><p><strong>What’s suspicious.</strong> A high-score beacon to a destination that only one internal host talks to. A beacon to a recently registered domain or to a bare IP. A beacon over HTTP(S) with a rare user-agent or JA4 fingerprint. A beacon that continues when the user is on leave.</p><p><strong>A note on evasion.</strong> Very long sleeps (hours), heavy jitter (50%+), domain fronting through CDNs and C2 over legitimate SaaS (Slack, Teams, Graph API, Google Sheets) all make beacon analysis harder. The defence is longer analysis windows, grouping by destination <em>domain</em> (from SNI or DNS) rather than only by IP, and comparing against how many other internal hosts use the same service. A beacon to graph.microsoft.com from every laptop is normal. A beacon to a specific SharePoint tenant from one server is not.</p><h3>Hunt 3: DNS — C2, tunnelling and abuse</h3><p><strong>The idea.</strong> <strong>DNS is the protocol everyone trusts and almost nobody blocks.</strong> Even locked-down networks that force web traffic through a proxy usually let internal resolvers forward queries to the internet. That makes DNS a favourite covert channel: tools such as dnscat2, iodine, and the DNS listeners in Cobalt Strike and Sliver encode data in query names and receive tasking in responses (MITRE ATT&amp;CK <a href="https://attack.mitre.org/techniques/T1071/004/">T1071.004</a> and <a href="https://attack.mitre.org/techniques/T1572/">T1572</a>).</p><p><strong>The weakness attackers cannot avoid.</strong> The <a href="https://activecm.github.io/threat-hunting-labs/dns/">DNS lab</a> explains it neatly: if the implant asks the same name twice, a resolver will answer from cache and the query never reaches the attacker’s name server. So every request needs a <strong>unique subdomain</strong>. <em>“The more requests an attacker sends out, the more unique subdomains they need to use.”</em> That gives us a simple, robust metric: <strong>unique subdomains per base domain</strong>.</p><p><strong>Step 1 — count unique subdomains per domain:</strong></p><pre>cat dns.log | zeek-cut query | sort | uniq \<br>  | rev | cut -d &#39;.&#39; -f 1-2 | rev \<br>  | sort | uniq -c | sort -nr | head</pre><p>This de-duplicates every query, trims each name to its last two labels (the base domain), and counts how many distinct names each base domain had. CDNs such as akamaiedge.net or akadns.net will appear with a few hundred or thousand. In the lab, the malicious domain r-1x.com had <strong>62,468</strong> unique subdomains. ACM&#39;s rule of thumb: a domain with <strong>more than 1,000 unique FQDNs</strong> deserves a look — especially if there are few or no actual connections to the resolved IPs.</p><p><em>Caveat:</em> cut -f 1-2 mishandles multi-part TLDs such as .co.id or .co.uk. For production use, parse with the Public Suffix List (e.g., Python&#39;s tldextract) → <a href="https://github.com/john-kurkowski/tldextract">https://github.com/john-kurkowski/tldextract</a>. RITA&#39;s DNS view also counts subdomains at every level, so dnsc.r-1x.com and r-1x.com are shown separately.</p><p><strong>Step 2 — look at the queries themselves:</strong></p><pre>cat dns.log | zeek-cut id.orig_h query qtype_name answers | grep &#39;r-1x\.com&#39; | head</pre><p>Red flags: long hexadecimal or base32/base64-looking labels (6dde0175375169c68f.dnsc.r-1x.com), labels near the 63-character limit, high entropy, fixed-length answers, and a steady rate that continues overnight.</p><p><strong>Step 3 — who is doing it, and is there a real connection?</strong></p><pre>cat dns.log | zeek-cut id.orig_h query | grep &#39;r-1x\.com&#39; | cut -f 1 | sort | uniq -c</pre><p>If the source is your internal resolver, you need the resolver’s own query logs to find the real client — one reason sensor placement matters. Then extract the answers and check conn.log for direct connections to those IPs. A domain with tens of thousands of lookups and <em>zero</em> follow-on connections is the textbook DNS tunnel.</p><p><strong>Step 4 — query-type distribution:</strong></p><pre>cat dns.log | zeek-cut qtype_name | sort | uniq -c | sort -nr</pre><p>Normal networks are dominated by A, AAAA, HTTPS/SVCB and some PTR. TXT, NULL and CNAME are favoured by tunnels because they carry more data per response. In the lab, the infected network showed <strong>108,911 TXT queries</strong>, compared with around 65 in a clean dataset of similar size.</p><p><strong>Other DNS hunts worth running:</strong></p><ul><li><strong>Rogue resolvers.</strong> Internal hosts sending DNS directly to an external server instead of your resolver (id.resp_p == 53 to a non-approved IP). A <strong>rogue resolver</strong> is when a company computer <strong>bypasses the corporate DNS</strong> and asks some external DNS server directly. If your policy says <strong>all DNS must go through the corporate resolver</strong>, this is worth investigating. Also look for DNS over HTTPS (DoH) to public providers from hosts that should use the corporate resolver.</li><li><strong>DGA and NXDOMAIN spikes.</strong> Malware using domain generation algorithms tries many random names and most fail. Count NXDOMAIN responses per host; a workstation with thousands of NXDOMAINs to random-looking .top, .xyz or similar TLDs is a strong lead.</li><li><strong>Newly registered domains.</strong> ACM notes that attackers favour domains <strong>less than 7 days old</strong>. Enrich your top-talker domains with WHOIS/RDAP creation dates.</li><li><strong>Fast flux.</strong> One domain resolving to many IPs across many ASNs with very short TTLs.</li></ul><h3>Hunt 4: Outliers and protocol anomalies</h3><p><strong>The idea.</strong> The first three hunts look for specific C2 behaviours. Outlier hunting is broader: find the hosts and pairs that are unusually high or low on some measure, then ask why. The technique is <strong>stack counting</strong> (also called long-tail analysis): count occurrences of a value across the whole environment and look at the extremes. Most of what you find will be benign, but you will learn your network — and occasionally the rare value is the attacker. The <a href="https://activecm.github.io/threat-hunting-labs/outliers/">outliers lab</a> covers the basics.</p><p><strong>4a — Connection count per pair:</strong></p><pre>cat conn.log | zeek-cut id.orig_h id.resp_h id.resp_p proto \<br>  | awk &#39;BEGIN{ FS=&quot;\t&quot; } { arr[$1 FS $2 FS $3 FS $4] += 1 } \<br>    END{ for (key in arr) printf &quot;%s%s%s\n&quot;, key, FS, arr[key] }&#39; \<br>  | sort -nrk 5 | head</pre><p>The top of this list is often DNS. That itself is interesting: caching should keep legitimate DNS volume to any single external server modest, so a host making tens of thousands of DNS connections to an unknown server is worth a second look.</p><p><strong>4b — Bytes transferred (exfiltration):</strong></p><pre>cat conn.log | zeek-cut id.orig_h id.resp_h orig_bytes \<br>  | awk &#39;BEGIN{ FS=&quot;\t&quot; } { arr[$1 FS $2] += $3 } \<br>    END{ for (key in arr) printf &quot;%s%s%s\n&quot;, key, FS, arr[key] }&#39; \<br>  | sort -nrk 3 | head</pre><p>orig_bytes is what the internal host <em>sent</em>. Workstations are normally consumers, not producers: they download far more than they upload. A useful refinement is the <strong>producer–consumer ratio</strong> (PCR), (orig_bytes − resp_bytes) / (orig_bytes + resp_bytes), which runs from −1 (pure download) to +1 (pure upload). A workstation that suddenly flips to a strongly positive PCR towards a single external destination — especially a cloud storage provider or VPS, especially outside working hours — is a classic exfiltration lead (ATT&amp;CK <a href="https://attack.mitre.org/techniques/T1048/">T1048</a>, <a href="https://attack.mitre.org/techniques/T1567/">T1567</a>).</p><p><strong>4c — Rare HTTP user-agents:</strong></p><pre>cat http.log | zeek-cut user_agent | sort | uniq -c | sort -n | head</pre><p>Sorting ascending puts the rarest user-agents at the top. In a well-patched fleet, browser user-agents should be fairly uniform. Rare strings point to unpatched systems, unauthorised software, scripts (python-requests, curl, Go-http-client) or malware with a hard-coded or slightly malformed UA. Then pivot:</p><pre>cat http.log | zeek-cut id.orig_h id.resp_h host uri user_agent | grep &#39;&lt;UA string&gt;&#39;</pre><p>Remember that the Host header is attacker-controlled. Cross-check it against dns.log: did this client actually resolve that name to that IP?</p><p><strong>4d — Port and protocol mismatch.</strong> Zeek identifies protocols by content (Dynamic Protocol Detection), not by port, and records the result in the service field. That lets you find:</p><ul><li>Non-standard ports for standard protocols — SSH on 443, HTTP on 8443 to a residential IP (ATT&amp;CK <a href="https://attack.mitre.org/techniques/T1571/">T1571</a>).</li><li>Standard ports carrying something else — traffic on 443 where service is empty or not ssl, or traffic on 53 that is not DNS.</li></ul><pre>cat conn.log | zeek-cut id.resp_p proto service | sort | uniq -c | sort -n | head -30</pre><p><strong>4e — TLS certificates and fingerprints.</strong> With most traffic encrypted, metadata is your friend. From ssl.log and x509.log look for:</p><ul><li><strong>Self-signed or untrusted certificates</strong> on outbound connections (validation_status in ssl.log). ACM treats a self-signed cert as a minor modifier towards &quot;suspicious&quot; and an Extended Validation cert as a modifier towards &quot;benign&quot;.</li><li><strong>Default C2 certificates.</strong> Unmodified framework defaults (for example, well-known default Cobalt Strike or Metasploit certificate subjects) still show up in real intrusions.</li><li><strong>Odd certificate attributes</strong> — very short or very long validity, random-looking subject names, free CA certs on brand-new domains, missing SNI.</li><li><strong>Rare client fingerprints.</strong> <a href="https://github.com/FoxIO-LLC/ja4">JA4+</a> (successor to JA3) fingerprints TLS clients (JA4), servers (JA4S), HTTP clients (JA4H), certificates (JA4X) and SSH (JA4SSH), and has a Zeek package. Stack-count JA4 values across the fleet: the browser fingerprints are common; a JA4 seen on only one host, talking to one destination, is interesting.</li></ul><h3>Bonus: east-west and lateral movement</h3><p>Everything above focuses on egress, because that is where C2 must appear. If you have internal sensors, the same stack-counting approach works for lateral movement (<a href="https://attack.mitre.org/tactics/TA0008/">TA0008</a>). Zeek’s smb_files.log, smb_mapping.log, dce_rpc.log, rdp.log, kerberos.log and ntlm.log let you ask:</p><ul><li>Which workstations talk SMB, RDP, WinRM (5985/5986) or SSH to <em>other workstations</em>? In most networks, workstation-to-workstation admin traffic should be near zero.</li><li>Which hosts call remote service or task-scheduler RPC interfaces (svcctl, atsvc, ITaskSchedulerService) — the footprint of PsExec-style execution and remote scheduled tasks?</li><li>Which hosts write to ADMIN$ or C$ shares?</li><li>Which host suddenly requests Kerberos service tickets for many SPNs (Kerberoasting) or DRSUAPI replication from a non-DC (DCSync)?</li></ul><h3>Pivot: from “suspicious pair” to a verdict</h3><p>A network finding is a lead, not a conclusion. Steps 3 and 4 of the method turn it into a decision.</p><p><strong>Evaluate the internal host.</strong></p><ul><li><strong>Identify it</strong>: DHCP lease, asset inventory, AD computer object, owner.</li><li><strong>Find the process:</strong> EDR network telemetry or Sysmon Event ID 3 (network connection) and Event ID 22 (DNS query) map the connection to a process, user and parent process.</li><li><strong>Check whether its other behaviour fits:</strong> is a printer making HTTPS beacons? Is a database server browsing the web?</li><li><strong>Passive fingerprinting</strong> (DHCP and TLS fingerprints, user-agents) helps when the host has no agent.</li></ul><p><strong>Evaluate the destination.</strong></p><ul><li><strong>Ownership: </strong>IP, ASN, hosting provider. Is it a business partner, a SaaS you use, or a VPS?</li><li><strong>Domain age and registration (WHOIS/RDAP)</strong>. Under 7 days is a strong indicator.</li><li><strong>Popularity:</strong> is it in a top-sites list (e.g., Tranco or Majestic Million)? Note that attackers abuse popular platforms too.</li><li><strong>Reputation:</strong> AbuseIPDB, Spamhaus, VirusTotal, abuse.ch feeds, your own CTI. Treat this as a <strong>minor</strong> modifier: crowdsourced lists contain false positives, and absence from a list proves nothing.</li><li><strong>How many other internal hosts talk to it?</strong> One out of 5,000 is very different from 4,000 out of 5,000.</li></ul><h3>Scoring and disposition</h3><p>ACM’s training proposes a simple, defensible way to make the call. Score each host from <strong>0 (safe) to 100 (compromised)</strong> using two kinds of evidence:</p><ul><li><strong>Major modifiers</strong> — strong integrity indicators: a persistent long connection or high-score beacon with no business reason, protocol misuse (tunnelling, port mismatch), DNS tunnelling patterns, large data transfers to a threat-intel-flagged destination.</li><li><strong>Minor modifiers</strong> — supporting clues: a rare user-agent or JA4, a self-signed certificate, a newly registered domain, a threat-intel hit on its own, a destination no other host uses.</li><li><strong>Negative modifiers</strong> — evidence of legitimacy: an EV certificate, a documented business relationship, a signed and expected process on the host.</li></ul><p>Add them up and choose one of three outcomes: <strong>allow-list</strong> (benign, record the reason), <strong>collect more data</strong> (uncertain: add PCAP, EDR, longer window), or <strong>escalate</strong> to incident response. The exact numbers matter less than consistency: two analysts looking at the same evidence should reach the same disposition.</p><h3>Quick ATT&amp;CK map for network hunts</h3><ul><li>Long connections, beacons — <a href="https://attack.mitre.org/techniques/T1071/">T1071</a> Application Layer Protocol, <a href="https://attack.mitre.org/techniques/T1573/">T1573</a> Encrypted Channel, <a href="https://attack.mitre.org/techniques/T1095/">T1095</a> Non-Application Layer Protocol</li><li>DNS C2 and tunnelling — <a href="https://attack.mitre.org/techniques/T1071/004/">T1071.004</a> DNS, <a href="https://attack.mitre.org/techniques/T1572/">T1572</a> Protocol Tunneling</li><li>DGA and fast flux — <a href="https://attack.mitre.org/techniques/T1568/">T1568</a> Dynamic Resolution</li><li>Port/protocol mismatch — <a href="https://attack.mitre.org/techniques/T1571/">T1571</a> Non-Standard Port</li><li>C2 via SaaS / CDNs — <a href="https://attack.mitre.org/techniques/T1102/">T1102</a> Web Service, <a href="https://attack.mitre.org/techniques/T1090/004/">T1090.004</a> Domain Fronting</li><li>Large uploads — <a href="https://attack.mitre.org/techniques/T1041/">T1041</a> Exfiltration Over C2 Channel, <a href="https://attack.mitre.org/techniques/T1048/">T1048</a>, <a href="https://attack.mitre.org/techniques/T1567/">T1567</a></li></ul><h3>Coming up in Part 3</h3><p>Reading commands is one thing; running them is another. In Part 3 we build a free hunting lab (Zeek, tshark, RITA, and full platforms such as Security Onion and Malcolm), pull real malicious traffic from public datasets, and work through five hands-on exercises that mirror the hunts in this article.</p><h3>References and further reading</h3><ul><li>Active Countermeasures — <a href="https://www.activecountermeasures.com/how-to-threat-hunt-your-network/">How to Threat Hunt Your Network</a></li><li>Active Countermeasures — <a href="https://www.activecountermeasures.com/wp-content/uploads/2021/02/Network-Threat-Hunting-202102.pdf">Network Threat Hunting training slides (PDF)</a></li><li>Threat Hunting Labs — <a href="https://activecm.github.io/threat-hunting-labs/long_connections/">Long Connections</a>, <a href="https://activecm.github.io/threat-hunting-labs/beacons/">Beacons</a>, <a href="https://activecm.github.io/threat-hunting-labs/dns/">DNS</a>, <a href="https://activecm.github.io/threat-hunting-labs/outliers/">Outliers</a></li><li><a href="https://github.com/activecm/rita">RITA on GitHub</a></li><li>FoxIO — <a href="https://github.com/FoxIO-LLC/ja4">JA4+ network fingerprinting</a></li><li><a href="https://attack.mitre.org/tactics/TA0011/">MITRE ATT&amp;CK — Command and Control (TA0011)</a></li><li>PuppyGraph — <a href="https://www.puppygraph.com/blog/network-threat-hunting">Network Threat Hunting</a></li><li>SANS — <a href="https://www.sans.org/cyber-security-courses/advanced-network-forensics-threat-hunting-incident-response">FOR572: Advanced Network Forensics</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=74c73587eb5e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/fmisec/hunting-on-the-wire-a-practical-guide-to-network-threat-hunting-part-2-74c73587eb5e">Hunting on the Wire : A Practical Guide to Network Threat Hunting — Part 2</a> was originally published in <a href="https://medium.com/fmisec">FMI Cyber Security Consulting Services</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Solving Flutter’s Proxy Issues with WireGuard]]></title>
            <link>https://medium.com/fmisec/solving-flutters-proxy-issues-with-wireguard-e8c6aef364b9?source=rss----5aebc5961dd0---4</link>
            <guid isPermaLink="false">https://medium.com/p/e8c6aef364b9</guid>
            <category><![CDATA[flutter-app-development]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[penetration-testing]]></category>
            <category><![CDATA[red-team]]></category>
            <category><![CDATA[flutter]]></category>
            <dc:creator><![CDATA[Patar Isac Pardomuan]]></dc:creator>
            <pubDate>Mon, 26 Oct 2026 02:01:02 GMT</pubDate>
            <atom:updated>2026-10-26T02:01:02.557Z</atom:updated>
            <cc:license>https://creativecommons.org/licenses/by-nc-sa/4.0/</cc:license>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*x9XAjOPfg6TAL4H14ZMAHQ.jpeg" /><figcaption>Cover Image Generated by Gemini</figcaption></figure><p><em>If you have ever tried to intercept network traffic from a Flutter application on a mobile device, you may know the frustration. You configure your device’s Wi-Fi proxy, set up your Burp Suite, and launch the app, only to watch the app function perfectly while your proxy history remains completely empty. That’s what I experienced when pentesting a Flutter-based mobile application.</em></p><h3>The Flutter’s Proxy Frustration</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*JuatMoN_2hXt2SkY" /><figcaption>Photo by <a href="https://unsplash.com/@punttim?utm_source=medium&amp;utm_medium=referral">Tim Gouw</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Unlike standard mobile applications, applications built with Flutter use Dart’s HttpClient, which completely ignores system-level proxy settings by default. It reaches out directly to the internet, making traditional Wi-Fi proxy setups useless for developers and security researchers.</p><p>This issue has been open since 2019 up to the time I write this, you can check it on <a href="https://github.com/flutter/flutter/issues/26359">https://github.com/flutter/flutter/issues/26359</a>.</p><p>You may know of or have even used reFlutter, but I don’t like that solution, and if you don’t like it either, we are the same. If you didn’t know about reFlutter, you can give it a try if you want.</p><p>To capture Flutter app traffic, we have to stop fighting the application layer and move down to the network layer.</p><p>On Android, it is pretty easy, we can utilize iptables for this.</p><p>But what about on iOS? Is there a solution that works on both Android and iOS?</p><p>We can utilize a WireGuard VPN. By routing our device through a WireGuard VPN tunnel, we force all device traffic to pass through our Burp Suite at the network layer. For my specific setup, I use a Linux machine because this solution relies heavily on iptables to manipulate the routing rules. It might be possible to achieve this natively on Windows or macOS using their respective networking tools, but I haven&#39;t tested it myself, so this article will focus entirely on the Linux approach.</p><h3>WireGuard Setup for Burp Suite Interception</h3><p>To force the mobile device’s traffic into our Burp Suite, we need to set up a WireGuard server and manipulate its routing table. The core of this technique lies in the PostUp and PostDown rules within the server&#39;s configuration file. These commands execute automatically when the VPN interface is brought up or torn down.</p><p>Here is the exact routing configuration required to capture the traffic:</p><pre>PostUp = sysctl -w net.ipv4.ip_forward=1<br>PostUp = iptables -t nat -A POSTROUTING -s 10.13.37.0/24 -o wlan0 -j MASQUERADE<br>PostUp = iptables -t nat -A PREROUTING -i %i -p udp --dport 53 -j DNAT --to-destination 1.1.1.1<br>PostUp = iptables -t nat -A PREROUTING -i %i -p tcp --dport 53 -j DNAT --to-destination 1.1.1.1<br>PostUp = iptables -t nat -A PREROUTING -i %i -p tcp -j REDIRECT --to-port 8083<br>PostUp = iptables -I FORWARD -i %i -j ACCEPT<br>PostUp = iptables -I FORWARD -o %i -j ACCEPT<br><br>PostDown = iptables -t nat -D POSTROUTING -s 10.13.37.0/24 -o wlan0 -j MASQUERADE<br>PostDown = iptables -t nat -D PREROUTING -i %i -p udp --dport 53 -j DNAT --to-destination 1.1.1.1<br>PostDown = iptables -t nat -D PREROUTING -i %i -p tcp --dport 53 -j DNAT --to-destination 1.1.1.1<br>PostDown = iptables -t nat -D PREROUTING -i %i -p tcp -j REDIRECT --to-port 8083<br>PostDown = iptables -D FORWARD -i %i -j ACCEPT<br>PostDown = iptables -D FORWARD -o %i -j ACCEPT<br>PostDown = sysctl -w net.ipv4.ip_forward=0</pre><p>Breaking down the firewall rules reveals exactly how the traffic hijacking works across any mobile device connected to the tunnel:</p><ul><li><strong>Enabling IP Forwarding:</strong> sysctl -w net.ipv4.ip_forward=1 instructs the Linux kernel to act as a router, allowing it to pass packets between network interfaces.</li><li><strong>Network Address Translation (NAT):</strong> The POSTROUTING rule takes traffic originating from the WireGuard subnet (10.13.37.0/24) and masquerades it out through the internet-facing interface (wlan0).</li><li><strong>The Interception Pivot:</strong> The REDIRECT --to-port 8083 rule intercepts all TCP traffic entering the WireGuard interface (%i) and forcefully redirects it to local port 8083. This is the port where Burp Suite must be configured to listen as an invisible proxy.</li><li><strong>Permitting Forwarded Traffic:</strong> The two FORWARD rules explicitly instruct the firewall to accept traffic flowing into and out of the WireGuard tunnel, ensuring packets are not dropped.</li><li><strong>Forcing Reliable DNS Resolution:</strong> The two DNAT rules targeting port 53 ensure that all DNS queries (both UDP and TCP) from the mobile device are forwarded directly to Cloudflare’s 1.1.1.1. This prevents DNS resolution failures or leaks while inside the tunnel.</li><li><strong>Clean Teardown:</strong> The PostDown rules reverse all of these changes when you stop the WireGuard server, ensuring your machine does not permanently alter its network behavior.</li></ul><h3>Generating Your WireGuard Configuration</h3><p>I built a simple static web app that generates these configurations for you. Just plug in your details, and the config will be automatically generated.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nJhHWYwmjl71R4BAr57bZQ.png" /><figcaption>WireGuard Config Generator User Interface — 1</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rpccA1hojo3E9LhHD0OeMg.png" /><figcaption>WireGuard Config Generator User Interface — 2</figcaption></figure><p>This tool is entirely static, nothing is saved, and no data goes to any database.</p><p>You can generate your configs right here: <a href="https://patarisac.github.io/wg-config/">patarisac.github.io/wg-config/</a></p><p>Or, if you prefer to run it locally on your own machine, you can grab the code here: <a href="https://github.com/patarisac/wg-config/">github.com/patarisac/wg-config/</a></p><p>If you find it useful, a star on the GitHub repo would make my day.</p><p>Once you apply these configs and connect to the VPN, your device’s traffic will finally start flowing into your Burp Suite.</p><h3>Wrapping Up</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*DZq4wn0_oJM0ffCq" /><figcaption>Photo by <a href="https://unsplash.com/@lukaskaufmann?utm_source=medium&amp;utm_medium=referral">Lukas Kaufmann</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Keep in mind that this WireGuard setup only handles the routing. It forces the traffic into your proxy, but it does not magically bypass SSL pinning. If the Flutter app enforces certificate pinning, you still have to use tools like Frida or Objection on a jailbroken or rooted device to bypass those pinning checks.</p><p>Also, treat this method as an alternative that is mainly useful for iOS. While it works perfectly on Android, it is honestly not necessary. If you have a rooted Android phone, it is much easier and faster to just use an adb shell and apply iptables rules directly on the device. But for an iPhone where Dart ignores your proxy and you can&#39;t just run local firewall commands, this WireGuard trick gets the job done.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e8c6aef364b9" width="1" height="1" alt=""><hr><p><a href="https://medium.com/fmisec/solving-flutters-proxy-issues-with-wireguard-e8c6aef364b9">Solving Flutter’s Proxy Issues with WireGuard</a> was originally published in <a href="https://medium.com/fmisec">FMI Cyber Security Consulting Services</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Exploiting JWT Vulnerabilities with Burp Suite JWT Editor]]></title>
            <link>https://medium.com/fmisec/exploiting-jwt-vulnerabilities-with-burp-suite-jwt-editor-c1e955c74f24?source=rss----5aebc5961dd0---4</link>
            <guid isPermaLink="false">https://medium.com/p/c1e955c74f24</guid>
            <category><![CDATA[web-app-pentesting]]></category>
            <category><![CDATA[jwt-hacking]]></category>
            <category><![CDATA[jwt]]></category>
            <category><![CDATA[jwt-vulnerabilities]]></category>
            <category><![CDATA[red-team]]></category>
            <dc:creator><![CDATA[Hakim Luqman Muhammad]]></dc:creator>
            <pubDate>Sat, 24 Oct 2026 13:56:52 GMT</pubDate>
            <atom:updated>2026-10-24T13:56:50.737Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*i-b6DScECpxuJk5TnvZnYw.png" /></figure><p>In this article, we will explore JWT attacks using the <a href="https://portswigger.net/bappstore/26aaa5ded2f74beea19e2ed8345a93dd">JWT Editor</a> extension for Burp Suite. We will demonstrate these attacks using the following PortSwigger labs:</p><ol><li><a href="https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-weak-signing-key">JWT authentication bypass via weak signing key</a></li><li><a href="https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-jwk-header-injection">JWT authentication bypass via JWK header injection</a></li><li><a href="https://portswigger.net/web-security/jwt/algorithm-confusion/lab-jwt-authentication-bypass-via-algorithm-confusion">JWT authentication bypass via algorithm confusion</a></li></ol><p>Each lab provides the credentials wiener:peter, and the objective is to access the /admin endpoint by exploiting JWT vulnerabilities.</p><h3>JWT Authentication Bypass via Weak Signing Key</h3><p>In this lab, the application uses a weak, guessable secret key to sign JWTs with the symmetric HS256 algorithm. This allows an attacker to discover the secret through a dictionary attack. Once the secret is recovered, the attacker can modify JWT claims, such as changing the sub claim from wiener to administrator, and re-sign the token to gain access to the admin interface.</p><p>After logging in with the wiener:peter credentials and accessing /admin, we receive an unauthorized response: Admin interface only available if logged in as an administrator.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aOOknnWq0qaGS2yTexnhAw.png" /><figcaption>Unauthorized Response</figcaption></figure><p>First we need to guess the JWT secret:</p><ol><li>Send the request to /admin to Repeater, then open the <strong>JSON Web Token</strong> tab.</li><li>Select <strong>Attack → Weak HMAC Secret.</strong></li><li>The HMAC secret is successfully recovered: secret1.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*o2pWV6iC2gXWOc6Bvw8Bvg.png" /><figcaption>Guess HS256 Secret with JWT Editor</figcaption></figure><p>Once the secret has been recovered, we need to create a key using that secret in the JWT Editor tab:</p><ol><li>Select <strong>New Symmetric Key</strong>.</li><li>Enter the recovered secret, secret1, in the <strong>Specify secret</strong> field.</li><li>Generate the key.</li><li>Note the generated key ID, ff43e26c-6efc-4a0b-a068-6753c36dc58d, and click <strong>OK</strong>.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/868/1*cB_M7ISX2RkRv1CtgF6ycA.png" /><figcaption>Generate Key</figcaption></figure><p>Finally, we can exploit the vulnerability by modifying the JWT claim and signing the token with the recovered secret. Return to the <strong>JSON Web Token</strong> tab in Repeater:</p><ol><li>Change the sub claim in the JWT payload from wiener to administrator.</li><li>Click <strong>Sign</strong> to sign the modified token using the key we created earlier.</li><li>Select the signing key with the ID ff43e26c-6efc-4a0b-a068-6753c36dc58d.</li><li>Select <strong>Don’t modify header</strong>.</li><li>Click <strong>OK</strong>, then send the request to /admin.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*T-gmjC07-euc6vzyblXlfw.png" /><figcaption>Signing JWT Token</figcaption></figure><p>The newly signed JWT can now be used to access /admin.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ROCjrD9cWW2PDFYpjBVQ4g.png" /><figcaption>Authentication Bypassed</figcaption></figure><h3>JWT Authentication Bypass via JWK Header Injection</h3><p>In this lab, the server trusts a public key embedded directly in the JWT header through the <strong>JWK</strong> parameter. An attacker can generate their own key pair, modify the JWT claims, and sign the token using their private key. If the server fails to verify whether the supplied public key is trusted, the attacker can use the forged JWT to gain access to the admin interface.</p><p>After logging in with the wiener:peter credentials and accessing /admin, we receive the following unauthorized response Admin interface only available if logged in as an administrator. To perform a JWK header injection attack, we first need to generate an RSA key in JWT Editor:</p><ol><li>Select <strong>New RSA Key</strong>.</li><li>Select <strong>JWK</strong>, then click <strong>Generate</strong>.</li><li>Note the generated key ID, 08815736-733b-44af-9295-c6e912051972, and click <strong>OK</strong>.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/848/1*Vw4Dipm3MqaR-G1Eg7zYhA.png" /><figcaption>Generate RS256 Key</figcaption></figure><p>Return to the <strong>JSON Web Token</strong> tab in Repeater. We will perform an embedded JWK attack by injecting our public key into the JWT header and signing the modified token with the corresponding private key.</p><ol><li>Change the sub claim in the JWT payload from wiener to administrator.</li><li>Select <strong>Attack → Embedded JWK</strong>.</li><li>Select the key we generated earlier with the ID 08815736-733b-44af-9295-c6e912051972.</li><li>Click <strong>OK</strong>, then send the request.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*W0CCqGYEUXHY0S2df2QgCA.png" /><figcaption>Embedded JWK Attack</figcaption></figure><p>The <strong>Attack → Embedded JWK</strong> feature automatically inject our public key, modifies the JWT header, and signs the token using corresponding private key. The resulting JWT can now be used to access /admin.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RISYNeEO0t-SOAHq5DxR8g.png" /><figcaption>Authenticatioin Bypassed</figcaption></figure><h3>JWT Authentication Bypass via Algorithm Confusion</h3><p>In this lab, the server uses the RS256 algorithm to verify JWT signatures. However, it incorrectly handles the RS256 public key as an HMAC secret. This implementation flaw allows an attacker to modify JWT claims and re-sign the token without knowing the server’s private key, potentially gaining access to the admin interface.</p><p>After logging in with the wiener:peter credentials and accessing /admin, we receive the following unauthorized response: Admin interface only available if logged in as an administrator. We can retrieve the public key from /jwks.json. The key has the ID b98fa760-e80d-4979-a902-fa2ea91f5344.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/948/1*VH-438mPEUx2dUvOGyUA_Q.png" /><figcaption>Public Key on /jwks.json</figcaption></figure><p>Note that we only need to extract the public key object shown below, rather than the entire JSON response:</p><pre>{<br>  &quot;kty&quot;: &quot;RSA&quot;,<br>  &quot;e&quot;: &quot;AQAB&quot;,<br>  &quot;use&quot;: &quot;sig&quot;,<br>  &quot;kid&quot;: &quot;b98fa760-e80d-4979-a902-fa2ea91f5344&quot;,<br>  &quot;alg&quot;: &quot;RS256&quot;,<br>  &quot;n&quot;: &quot;st0Wb1r75-noLvBO_F5e1AWQNfrqbA-2InNJVHA1N75bbFtuP6a5VY1k-tIPHRo6A4GqSmfJFk7QyodZ-KuQIfLgqc2YrtmH0R0OwIQIRGpSJGe-C4YDSAeA-tEsD58GZyy7Ksj0nGfg210INLOAP_YMnu59dLnGX8bg6pxA6QDIZ2-6k3phUbF9S0xngqPTRlWfRwLJQxy_G5nLRlOorrE-GqA19Wy00cHZ_hY9AqpWiefR6bs_JuPzEx6mfxNujs3NlRspTiwvJb2087uGGPBrCQJCcqz_J0eSfRyCpwvYkZ8tEGy9unXaWLhB1-pypE0oG9fP8W_ChQRPGJRYgw&quot;<br>}</pre><p>We then need to import the public key into JWT Editor:</p><ol><li>Select <strong>New RSA Key</strong>.</li><li>Paste the public key.</li><li>Confirm that the key ID is b98fa760-e80d-4979-a902-fa2ea91f5344, then click <strong>OK</strong>.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/846/1*2tPgCAyAzPF6KGDDg-_qEg.png" /><figcaption>Create RSA Public Key</figcaption></figure><p>Return to the <strong>JSON Web Token</strong> tab for the /admin request in Repeater. After importing the key, we can perform an HMAC key confusion attack by using the public key as the HS256 signing secret.</p><ol><li>Change the sub claim in the JWT payload from wiener to administrator.</li><li>Select <strong>Attack → HMAC Key Confusion</strong>.</li><li>Select the imported key with the ID b98fa760-e80d-4979-a902-fa2ea91f5344.</li><li>Keep the default options. If the attack fails, try different options.</li><li>Click <strong>OK</strong>, then send the request.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7KTIWOxD41Qs49QsBIimew.png" /><figcaption>Algorithm Confusion Attack</figcaption></figure><p>The <strong>Attack → HMAC Key Confusion</strong> feature automatically signs the modified token using the HS256 algorithm, treating the imported public key as the HMAC secret. The resulting JWT can now be used to access /admin.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HEA2JmprvuMWODZE7-O6Ng.png" /><figcaption>Authentication Bypassed</figcaption></figure><h3>Conclusion</h3><p>These three labs demonstrate how weak signing secrets, improper public key validation, and JWT algorithm confusion can allow attackers to forge tokens and bypass authentication. Using JWT Editor in Burp Suite makes it easier to test these attack scenarios.</p><p>To mitigate these vulnerabilities, applications should use strong cryptographic keys, enforce an explicit allowlist of permitted algorithms, validate the source of signing keys, and implement proper server-side authorization checks.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c1e955c74f24" width="1" height="1" alt=""><hr><p><a href="https://medium.com/fmisec/exploiting-jwt-vulnerabilities-with-burp-suite-jwt-editor-c1e955c74f24">Exploiting JWT Vulnerabilities with Burp Suite JWT Editor</a> was originally published in <a href="https://medium.com/fmisec">FMI Cyber Security Consulting Services</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[I Printed a Meme on My Client’s Printer]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/fmisec/i-printed-a-meme-on-my-clients-printer-f92f90a4081d?source=rss----5aebc5961dd0---4"><img src="https://cdn-images-1.medium.com/max/2600/1*nGHCLUVI2_-Ir7L5JyyxEg.jpeg" width="2752"></a></p><p class="medium-feed-snippet">For about ten seconds I genuinely felt like the hacker in every movie ever.</p><p class="medium-feed-link"><a href="https://medium.com/fmisec/i-printed-a-meme-on-my-clients-printer-f92f90a4081d?source=rss----5aebc5961dd0---4">Continue reading on FMI Cyber Security Consulting Services »</a></p></div>]]></description>
            <link>https://medium.com/fmisec/i-printed-a-meme-on-my-clients-printer-f92f90a4081d?source=rss----5aebc5961dd0---4</link>
            <guid isPermaLink="false">https://medium.com/p/f92f90a4081d</guid>
            <category><![CDATA[infrastructure-pentest]]></category>
            <category><![CDATA[hacking-printers]]></category>
            <category><![CDATA[pentesting]]></category>
            <category><![CDATA[pentest-infra]]></category>
            <dc:creator><![CDATA[abay - Akbar Kustirama]]></dc:creator>
            <pubDate>Sat, 24 Oct 2026 13:54:59 GMT</pubDate>
            <atom:updated>2026-10-24T14:01:14.176Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Adversarial Detection Engineering in the Real SOC: A Practitioner’s Guide to the ADE Framework]]></title>
            <link>https://medium.com/fmisec/adversarial-detection-engineering-in-the-real-soc-a-practitioners-guide-to-the-ade-framework-0dd981c99872?source=rss----5aebc5961dd0---4</link>
            <guid isPermaLink="false">https://medium.com/p/0dd981c99872</guid>
            <category><![CDATA[siem]]></category>
            <category><![CDATA[adversary-simulation]]></category>
            <category><![CDATA[detection-as-code]]></category>
            <category><![CDATA[security-operations]]></category>
            <category><![CDATA[detection-engineering]]></category>
            <dc:creator><![CDATA[Digit Oktavianto]]></dc:creator>
            <pubDate>Wed, 21 Oct 2026 02:01:04 GMT</pubDate>
            <atom:updated>2026-10-21T02:01:03.973Z</atom:updated>
            <content:encoded><![CDATA[<h3>The rule that was always going to miss</h3><p>Most detection rules fail silently. They sit in production, green and healthy on the dashboard, and the activity they were written to catch walks straight past them without raising a single alert. No error, no exception, no log line that says <em>I just let that through</em>. The SOC believes it has coverage. It does not.</p><p>That gap — between what a rule <em>intends</em> to catch and what it <em>actually</em> catches — is the subject of <strong>Adversarial Detection Engineering (ADE) </strong>(<a href="https://github.com/Adversarial-Detection-Engineering">https://github.com/Adversarial-Detection-Engineering</a>), an open-source framework created by Nikolas Bielski (with co-maintainer Daniel Koifman) that treats the false negative as a first-class engineering defect rather than an accident of tuning. <a href="https://adeframework.org/docs">ADE</a> gives that defect a name: a <strong>detection-logic bug</strong>. And, like any bug class, once you can name it you can hunt it, classify it, test for it, and fix it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dLv_7rOL_VYgAfF0AudzQw.jpeg" /><figcaption>Image Generated by Gemini</figcaption></figure><p>This post is written for the detection engineer, the SOC analyst moving into rule authoring, and the purple teamer who wants a shared vocabulary with the blue team. It covers three things, in order: what the ADE framework actually says and where it fits among the detection-engineering frameworks you already know; how to run it as a repeatable workflow inside a live SOC; and which platforms and tools — Sigma, Atomic Red Team, Caldera, your SIEM, your CI/CD pipeline — you bolt it onto, with the practical mechanics of each.</p><p>The mindset shift ADE asks for is small but load-bearing. Stop asking <em>“does my rule detect the technique?”</em> Start asking <em>“what is the full set of things that satisfy my rule’s intent, and which members of that set does my query actually match?”</em> The difference between those two questions is where attackers live.</p><h3>What ADE actually is: a bug taxonomy for detection logic</h3><p>The conceptual core is borrowed, deliberately, from software engineering. ADE models a <a href="https://adeframework.org/docs">detection rule as a repeatedly-running hypothesis test</a>: it takes a bucket of records and tests each against a set of conditions. The rule has a <strong>scope</strong> — the human-readable intent, carried in its description and MITRE ATT&amp;CK mappings (“detect PowerShell downloading a file”) — and a <strong>detection logic</strong>, the machine-interpretable query that is supposed to implement that intent. A <strong>detection-logic bug</strong> is a mismatch between the two: an attacker behavior that falls squarely inside the rule’s scope, yet does not trip the query. In confusion-matrix terms, a ground-truth true positive the rule reports as negative. A false negative, caused on purpose.</p><p>The framework’s contribution is that it stops treating those misses as one undifferentiated blob and sorts them into a taxonomy: <strong>four categories, sixteen subcategories, and (at time of writing) thirty-eight worked technique files</strong>, each grounded in real public rules from Sigma, Elastic Security and Microsoft Sentinel. <strong>The four categories examples</strong> below are the load-bearing structure — learn these and you can place almost any miss:</p><ol><li><strong>ADE1 — Reformatting in Actions.</strong> The logged input is reshaped so a string match fails, even though the behavior is unchanged. Substring manipulation, obfuscation, whitespace and delimiter tricks, normalization asymmetry. The classic: a rule keys on the literal .DownloadString(, and the attacker writes $wc.(&quot;Download&quot;+&quot;String&quot;)(...). Same download, no match.</li><li><strong>ADE2 — Omit Alternatives.</strong> The rule enumerates one member of an equivalence class and misses the rest. A different LOLBin, a renamed binary, a newer API version, an alternate path, a different file type — all in scope, none listed. If your rule anchors on curl.exe, it says nothing about certutil, bitsadmin, BITS jobs, or a .NET WebClient.</li><li><strong>ADE3 — Context Development.</strong> The attacker shapes the <em>surrounding context</em> rather than the primary action: cloning a process name, hijacking an aggregation threshold, spreading activity past a correlation window (low-and-slow), fragmenting one logical action across several events, spoofing parent lineage, or saturating an engine’s join/groupBy limit so the needed record is silently truncated.</li><li><strong>ADE4 — Logic Manipulation.</strong> The Boolean, filter or field logic is itself wrong or invertible: an AND that should be OR, a negated filter on an attacker-controlled field, or a field that doesn&#39;t resolve on the deployed backend and so matches nothing — making the rule structurally blind before an attacker even shows up.</li></ol><p>Two supporting tools in the repo turn the taxonomy into something you can run. The <a href="https://adeframework.org/docs"><strong>Bug Likelihood Test</strong></a> is a fast pre-analysis checklist — roughly a dozen yes/no questions (“Is there a process-name string match that could be renamed?” → ADE3–01; “Are there aggregation operators like &gt;=?” → ADE3-02) that triages a rule in minutes and tells you which category to dig into. The <strong>ADE False-Negative Checklist</strong> applies four lenses to every rule you review: data-source dependency (does the telemetry even reach the rule?), threshold assumptions, scope assumptions, and environment drift.</p><p>One point the authors are emphatic about, and so am I: this is a <strong>defensive, purple-team framework</strong>. Every lens is answered at the level of <em>“would this rule catch the technique, and what must be true for it to fire?”</em> — never <em>“here is the exact recipe to beat rule X.”</em> You describe <em>categories</em> of gap so the blue team can close them. Weaponizing it against systems you aren’t authorized to test is a different activity with a different name.</p><h3>Where ADE sits in the detection-engineering stack</h3><p>ADE doesn’t replace the frameworks you already run; it fills a specific hole none of them were built for. It helps to see the whole stack and what each layer answers.</p><p><a href="https://attack.mitre.org/"><strong>MITRE ATT&amp;CK</strong></a> tells you <em>what</em> to detect — the technique catalog and the common vocabulary everything else maps to. <strong>The </strong><a href="https://www.sans.org/tools/the-pyramid-of-pain/"><strong>Pyramid of Pain</strong></a> (David Bianco) tells you <em>how durable</em> a given detection is: hashes and IPs are trivial for an attacker to change, while TTPs are painful, so you should aim your logic high on the pyramid. <a href="https://github.com/palantir/alerting-detection-strategy-framework"><strong>Palantir’s Alerting and Detection Strategy (ADS)</strong></a> tells you <em>how to document</em> a detection to a quality bar — its template forces a Strategy Abstract, an ATT&amp;CK categorization, and, tellingly, an explicit <strong>Blind Spots and Assumptions</strong> section where the Detection Engineer records where the alert may not fire. <a href="https://medium.com/@tahirbalarabe2/what-is-detection-as-code-how-to-implement-detection-as-code-ae8e3bac22f7"><strong>Detection-as-Code</strong></a> tells you <em>how to ship and govern</em> detections: version control, peer review, linting, CI/CD, automated testing.</p><p>What none of those does, at the level of query mechanics, is tell you <em>why a specific, reasonable-looking rule won’t fire against an in-scope variant.</em> ADS gives you a box labeled “Blind Spots and Assumptions”; ADE gives you the taxonomy to fill it in rigorously instead of from memory. The Pyramid of Pain tells you to detect behavior rather than strings; ADE1 and ADE2 show you the precise ways a behavior-named-as-a-string still collapses back into a brittle IOC. In the language SpecterOps popularized in <a href="https://posts.specterops.io/capability-abstraction-fbeaeeb26384"><em>Capability Abstraction</em></a> and <em>On Detection: Tactical to Functional</em> — the idea that a technique has an abstraction chain from tool down to the operation it actually performs — ADE is a structured way to walk down that chain and find which rung your detection accidentally pinned itself to.</p><p>It also isn’t alone in treating adversarial review as central. The emerging work on automated detection engineering — for example Mitiga’s <a href="https://www.mitiga.io/blog/automated-detection-engineering-forge">FORGE</a>, which runs a generate-then-adversarially-validate pipeline with a separate reviewer agent — is the same instinct ADE formalizes by hand: a detection is not done when it’s written, it’s done when something has actively tried to make it miss and failed.</p><p>The table below is the one-glance version.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*K2h4eNJtTcAx_if8JW5T1Q.png" /><figcaption>Table of Detection Engineering Framework</figcaption></figure><p>In short: the other frameworks make detections <em>well-documented, durable and shippable</em>. ADE makes them <strong>robust against someone who read them.</strong></p><h3>Running ADE in a live SOC: the workflow</h3><p>The framework is only useful if it becomes a habit your team repeats, not a one-off audit. Here is how to operationalize it. Treat it as a loop you run against your detection backlog continuously, and bolt onto every new rule before it ships.</p><p><strong>Who does it.</strong> This is where ADE gives purple teaming a shared language. The detection engineer owns the rule and the fix. The red/purple teamer owns the emulation that proves the gap is real. An analyst or threat hunter often spots the candidate gap first, from an incident where a rule “should have” fired. The ADE taxonomy is the lingua franca between them — a miss gets a label (ADE3–01), not a shrug.</p><p><strong>The loop, step by step:</strong></p><ol><li><strong>Select the rule and state its scope.</strong> Pull one production (or candidate) rule. Write down, in one sentence, what it intends to catch and its ATT&amp;CK mapping. If you can’t state the scope crisply, that’s finding zero — an ambiguous rule can’t be reasoned about.</li><li><strong>Run the Bug Likelihood Test.</strong> Walk the dozen checklist questions. Does it string-match a command line (ADE1–01)? Anchor on one binary or method (ADE2–01)? Use a threshold or time window (ADE3–02/03)? Key on parent process (ADE3–05)? Negate a filter on an attacker-controlled field (ADE4–01)? Each “yes” is a hypothesis about where a false negative lives. This takes minutes and tells you whether the rule is worth a deep review.</li><li><strong>Apply the four lenses.</strong> For each flagged area, ask the checklist’s questions properly. Data-source dependency first — <em>is the telemetry this rule needs actually ingested on the in-scope hosts?</em> A rule that assumes Sysmon EID 1 on a fleet running only Security 4688 (with command-line auditing off by default) contributes zero coverage, silently. Then threshold, scope, and environment-drift assumptions.</li><li><strong>Form the adversarial hypothesis — at the category level.</strong> For each gap, write it as a testable statement: <em>“The same technique performed with an alternative in-scope binary / reformatted command / below the count threshold / spread past the window is predicted to produce no alert.”</em> Note: this is a <em>category</em> of variant, routed to your authorized red-team lead to turn into a concrete test under rules of engagement — not an execution recipe you write into the ticket.</li><li><strong>Validate in a lab, under authorization.</strong> Emulate the variant on a monitored test host and check whether the rule fired. This is the step that converts a suspected bug into a confirmed one. Atomic Red Team and Caldera (next section) are how you do it repeatably.</li><li><strong>Harden the rule and re-test.</strong> Apply the fix — broaden the match, OR in alternatives, switch to an immutable field, add a presence-guard to a negated filter, add a companion “canary” rule for the evasion itself — then re-run the same emulation to prove the gap closed and nothing regressed.</li><li><strong>Document honestly and record residual blind spots.</strong> Update the rule’s ADS-style record: what you tested, what’s now covered, and what you still can’t detect. “Not covered: direct registry modification — needs a separate rule” is a valid, valuable finding. A rule that passes all four lenses gets a one-line note saying so and a reminder to confirm it’s actually enabled.</li></ol><p><strong>Cadence.</strong> New rules get steps 1–4 as a pre-merge gate (cheap, fast, no lab needed). Existing rules get the full loop on a rotation — prioritize by ATT&amp;CK coverage importance and by Bug Likelihood score. Tie it to your purple-team calendar: each emulation exercise should close its confirmed gaps back into the rule base, so the exercise produces hardened detections, not just a report.</p><p>The discipline that makes this work is <strong>adversarial review by someone other than the Detection Engineer themselves.</strong> You are biased toward the rule you just wrote; you will read your own query and see the technique it catches, not the three it misses. Have a second engineer (or, increasingly, an automated reviewer) run the lenses cold.</p><h3>The toolchain: what to pair with ADE, and how</h3><p>ADE is a reasoning framework, not a product. Its value shows up when you wire it into tools you (probably) already run. Here is the practical mapping, in the order you’d use them around the loop.</p><h3>1. Sigma — the portable rule you reason about</h3><p><a href="https://github.com/SigmaHQ/sigma">Sigma</a> is the default starting point, and the repo’s own bug-pattern references lean on it. Because a Sigma rule is backend-agnostic YAML that transpiles to most SIEM languages, it’s the cleanest artifact to read <em>“how is this technique usually caught”</em> and to apply the taxonomy to.</p><p><em>How to use it with ADE:</em> Read the detection: block as the hypothesis test. A list under one key is OR; separate keys are AND; |all forces AND across a list. Most ADE4 (logic) bugs are visible right here — the infamous example is a rule that ANDs Net.WebClient with (.DownloadString( OR .DownloadFile(), where the fix is simply condition: 1 of selection_*. Then check the ADE1 reformatting modifiers: is there a |windash for -// prefixes? A |base64offset for encoded commands? An OR onto OriginalFileName to resist renaming? Crucially, <strong>transpilation is not lossless</strong> — a Sigma rule&#39;s real behavior depends on the backend (regex flavor, case sensitivity, null-handling on not filter). Always inspect the <em>compiled</em> query on your actual backend, not the YAML. Field typos (TargetFileName vs the correct TargetFilename) resolve to no column and fail silently — a pure ADE4-04 bug.</p><h3>2. Your SIEM/EDR — where the bug is real or not</h3><p>Elastic Security, Microsoft Sentinel, Splunk, and CrowdStrike LogScale each have public rule repos and each has platform-specific traps the ADE bug_patterns/ catalog documents.</p><p><em>How to use it with ADE:</em> map the generic category to the concrete fix for your platform. A few high-leverage examples straight from the framework’s mitigations guidance:</p><ul><li><strong>Elastic </strong><strong>threshold rules (ADE3-02):</strong> don&#39;t count per attacker-controlled field alone; add threshold.cardinality (distinct sources against a target) and a coarser account/tenant bucket so distributing activity across entities doesn&#39;t duck under.</li><li><strong>Elastic </strong><strong>eql sequence (ADE3-03):</strong> set a realistic maxspan, not 5s, or decouple the steps — otherwise a sleep expires the correlation.</li><li><strong>Sentinel aggregation (ADE3–02):</strong> avoid per-IPAddress thresholds and Top-N ranking; use cardinality and absolute thresholds, and corroborate with an orthogonal signal.</li><li><strong>Sentinel case sensitivity (ADE1/ADE4):</strong> ==, has, in are case-sensitive in KQL and silently miss variants; use =~, in~, has_cs deliberately.</li><li><strong>Process identity everywhere (ADE3–01):</strong> anchor on process.executable / hash / code-signature plus args, never process.name alone, which any rename defeats.</li><li><strong>Negated filters everywhere (ADE4–01/04):</strong> guard every not filter on an optional field with a presence check (field|exists: true), so an absent field can&#39;t invert the rule.</li></ul><h3>3. Atomic Red Team — cheap, fast variant validation</h3><p><a href="https://github.com/redcanaryco/atomic-red-team">Atomic Red Team</a> (Red Canary) is a library of small, ATT&amp;CK-mapped tests you run on a host to generate the telemetry a technique produces. It’s the fastest way to execute step 5 of the loop.</p><p><em>How to use it with ADE:</em> pick the atomic test(s) for your technique, run them on a monitored lab host, and confirm the baseline rule fires. Then use the <em>variants</em> — many atomics parameterize the binary, path or arguments, which is exactly ADE2 (alternatives) and ADE2–03 (locations) made executable. Run the canonical form, confirm the alert; run the in-scope variant, check whether the alert <em>disappears</em>. A disappearing alert is a confirmed detection-logic bug. Where no atomic exists for the exact variant, that’s the cue to route it to your red-team lead rather than improvising one in the SOC.</p><h3>4. Caldera — chained, context-shaping emulation</h3><p><a href="https://github.com/mitre/caldera">MITRE Caldera</a> runs autonomous adversary profiles — sequences of abilities, not single atomics. It’s the right tool for the ADE3 (Context Development) category, where the bug only appears across a chain: lineage spoofing, event fragmentation, low-and-slow timing, aggregation across entities.</p><p><em>How to use it with ADE:</em> build (or adapt) an adversary profile that performs the in-scope technique the “ordinary” way, verify your correlation/sequence rules fire, then adjust the profile’s timing or structure to probe an ADE3 hypothesis — e.g. introduce a delay that exceeds a correlation window, or split a piped command so the malicious string never lands in one process-creation event. Caldera’s planners and fact system let you exercise the <em>context</em> an ADE3 rule depends on, which single atomics can’t reach.</p><h3>5. CI/CD + Detection-as-Code — make it non-optional</h3><p>The mitigations guidance is explicit: <em>build automated bypass testing into your CI/CD pipeline.</em> This is where ADE stops being an audit and becomes a guardrail.</p><p><em>How to use it with ADE:</em> store detections as code (Sigma YAML or your platform’s native format) in version control. On every pull request, run a pipeline that (a) lints the rule and validates field names against a known schema — catching ADE4–04 silent-field bugs before merge; (b) transpiles and diffs the compiled query; and © in a detection test lab, replays the relevant Atomic Red Team tests, including the variant set, and fails the build if the canonical test fires but a known in-scope variant doesn’t. The Bug Likelihood checklist can even live as a PR template — the Detection Engineer ticks the boxes, the reviewer checks them cold.</p><h3>6. LLM-assisted review — the newest layer</h3><p>The ADE repo itself ships a <a href="https://github.com/samugit83/redamon">Redamon</a> skill (ade_scoper) that packages the taxonomy for an agent to plan detection-aware emulation; the broader trend (Mitiga&#39;s FORGE and similar) points the same way — a generate-then-adversarially-validate loop with a <em>separate</em> reviewer model, precisely because a model, like a human, shouldn&#39;t grade its own detection. Used carefully and kept inside authorization and human go/no-go, an LLM is a useful fresh-eyes reviewer for running the four lenses across a large rule base — but it&#39;s an accelerant for the workflow above, not a replacement for the lab validation that actually proves a bug exists.</p><h3>A worked example, end to end</h3><p>Let’s run one rule through the loop so the abstractions land. Take the real Sigma rule <strong>Suspicious PowerShell Download</strong> (id: 3236fcd0-...), which the ADE docs themselves dissect.</p><p><strong>Step 1 — Scope.</strong> Intent: detect a PowerShell script downloading a file. ATT&amp;CK: T1059.001 (Command and Scripting Interpreter: PowerShell).</p><p><strong>Step 2 — The hypothesis test.</strong> The detection: block requires Data|contains: &#39;Net.WebClient&#39; <strong>AND</strong> (Data|contains: &#39;.DownloadString(&#39; <strong>OR</strong> &#39;.DownloadFile(&#39;). In Boolean terms: Hit when A AND (B OR C).</p><p><strong>Step 3 — Bug Likelihood Test fires twice.</strong> There’s a substring match on an attacker-controllable field → <strong>ADE1–01 (Substring Manipulation)</strong>. And it enumerates only two download methods when PowerShell offers many → <strong>ADE2–01 (Method/Binary)</strong>.</p><p><strong>Step 4 — Adversarial hypotheses (category level).</strong></p><ul><li><em>ADE1–01:</em> the literal .DownloadString( includes the . and (. A script that constructs the method name as a string and invokes it dynamically never produces that exact substring — predicted no match, same download.</li><li><em>ADE2–01:</em> Net.WebClient is one of several download primitives. A script reaching the same outcome through a different .NET member or reflection isn&#39;t enumerated — predicted no match.</li></ul><p><strong>Step 5 — Validate.</strong> In the lab, run the canonical form (plain WebClient.DownloadString) and confirm the alert fires. Then run the reformatted and alternative-method variants and watch the alert not appear. Both bugs confirmed.</p><p><strong>Step 6 — Harden.</strong> The root cause is chaining Net.WebClient <em>before</em> the OR. The repo&#39;s own fix is to break that dependency:</p><pre>condition: 1 of selection_*</pre><p>… so either the WebClient indicator <em>or</em> a download-method indicator alone can trip the rule. That closes the immediate AND trap. But the review isn&#39;t done: Net.WebClient and the method names can <em>still</em> be reformatted or swapped, so you layer defenses — add reformatting-tolerant modifiers, OR in additional download primitives, and keep a behavioral companion rule (e.g. a Sysmon EID 15 Zone.Identifier / ZoneId=3 rule that catches the <em>outcome</em> — a file marked as internet-downloaded — regardless of the method used to fetch it). That outcome-based rule is the Pyramid-of-Pain move: it keys on something the attacker can&#39;t trivially reshape.</p><p><strong>Step 7 — Document.</strong> Record the two bugs, the fix, the companion rule, and the residual: <em>exotic download primitives not in the OR list remain uncovered by the string rule; the Sysmon EID 15 rule is the backstop; re-test on next review.</em> That honest blind-spot note is the whole point — the next engineer inherits the truth, not a false sense of coverage.</p><p>Two bugs, one afternoon, a hardened rule plus a backstop, and a documented residual. That’s the loop at its most ordinary — and most SOCs have hundreds of rules with exactly this shape waiting to be run through it.</p><h3>Metrics, and the pitfalls that bite</h3><p>The hard part of false negatives is that they don’t show up in the usual SOC metrics. Alert volume, mean-time-to-respond, true-positive rate — none of them can see a rule that never fires. So you have to measure the <em>process</em>, not just the outcomes.</p><p>Useful signals to track: the share of production rules that have been through an ADE review; the count of confirmed detection-logic bugs found and closed per cycle; <strong>rules that have never fired since deployment</strong> (a strong smell of an ADE4–04 silent-field or ADE1 data-source bug — the mitigations guide explicitly says to flag these for review); coverage of in-scope ATT&amp;CK techniques <em>validated by emulation</em> rather than just mapped on paper; and the Bug Likelihood score distribution across the rule base, so you can prioritize. On the maturity side, the Detection Engineering Maturity Matrix and the DEBMM give you a program-level benchmark; ADE slots in as the thing that moves you from <em>“we write and map rules”</em> to <em>“we validate rules against an adversary who read them.”</em></p><p>The pitfalls worth naming up front:</p><ul><li><strong>Confusing a rule that exists with coverage that exists.</strong> The single most common ADE finding is that the telemetry a rule needs isn’t collected on the hosts in question. Check data-source dependency <em>first</em>, every time.</li><li><strong>Over-fitting the fix to the one tested bypass.</strong> Broadening a rule to catch exactly the variant you tested just moves the goalposts by one. Fix the <em>class</em> — switch to an immutable field, invert positive allow-lists into alert-broadly-suppress-trusted — not the single string.</li><li><strong>Grading your own homework.</strong> Detection Engineer run ADE reviews catch far less than a second set of eyes. Build adversarial review into the process structurally.</li><li><strong>Letting it drift into offense.</strong> Keep every finding at the category level and inside authorization. The framework’s own guardrails are clear: detection rules are generally out of scope for vendor disclosure, so handle examples responsibly, and route anything that starts to look like an execution recipe to the authorized red-team lead.</li><li><strong>One-and-done.</strong> Attackers evolve, LOLBins get discovered, APIs get versioned, your environment drifts. A rule that was robust last quarter may not be now. ADE is a loop, not an audit.</li></ul><h3>Closing: detect like someone is reading your rules</h3><p>The assumption baked into a lot of detection content is that the attacker doesn’t know how they’re being watched. That assumption has never been true, and ADE is what it looks like to stop pretending otherwise. The framework’s real gift isn’t the sixteen subcategories — it’s the reflex: <em>every rule is a hypothesis, and a motivated adversary gets to pick the test case.</em> Once a team internalizes that, the four categories become second nature and the loop becomes muscle memory.</p><p>Start small. Take ten of your highest-priority production rules, run the Bug Likelihood Test on each this week, and pick the worst offender to drive through the full loop with an atomic test. You will almost certainly confirm at least one silent false negative — and that single closed bug is worth more than another dashboard.</p><h3>Resources</h3><p><strong>The framework</strong></p><ul><li><a href="https://adeframework.org/docs">ADE Framework docs</a> — the theory, taxonomy, and checklists</li><li><a href="https://github.com/Adversarial-Detection-Engineering/adeskills">adeskills repository</a> — 38 worked technique files, per-platform bug-pattern catalogs, ruleset search guidance, the Redamon ade_scoper skill</li></ul><p><strong>Complementary frameworks</strong></p><ul><li><a href="https://github.com/palantir/alerting-detection-strategy-framework">Palantir Alerting &amp; Detection Strategy (ADS)</a> — the detection documentation template, incl. Blind Spots &amp; Assumptions</li><li><a href="https://attack.mitre.org/">MITRE ATT&amp;CK</a> — the technique catalog everything maps to</li><li><a href="https://www.sans.org/tools/the-pyramid-of-pain/">The Pyramid of Pain</a> — David Bianco, on indicator durability</li><li><a href="https://posts.specterops.io/capability-abstraction-fbeaeeb26384">SpecterOps — Capability Abstraction</a> — the abstraction-chain view of a technique</li></ul><p><strong>Tools</strong></p><ul><li><a href="https://github.com/SigmaHQ/sigma">SigmaHQ/sigma</a> — portable detection rules</li><li><a href="https://github.com/redcanaryco/atomic-red-team">Atomic Red Team</a> — ATT&amp;CK-mapped atomic tests</li><li><a href="https://github.com/mitre/caldera">MITRE Caldera</a> — chained adversary emulation</li><li>Public rule repos to read real logic: <a href="https://github.com/elastic/detection-rules">Elastic detection-rules</a>, <a href="https://github.com/splunk/security_content">Splunk security_content</a>, <a href="https://github.com/Azure/Azure-Sentinel">Azure-Sentinel</a>, <a href="https://github.com/CrowdStrike/logscale-community-content">CrowdStrike logscale-community-content</a></li></ul><p><strong>Further reading on the discipline</strong></p><ul><li><a href="https://www.vectra.ai/topics/detection-engineering">What is Detection Engineering (Vectra)</a> — lifecycle and framework comparison</li><li><a href="https://www.mitiga.io/blog/automated-detection-engineering-forge">Automated Detection Engineering: FORGE (Mitiga)</a> — generate-then-adversarially-validate pipelines</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0dd981c99872" width="1" height="1" alt=""><hr><p><a href="https://medium.com/fmisec/adversarial-detection-engineering-in-the-real-soc-a-practitioners-guide-to-the-ade-framework-0dd981c99872">Adversarial Detection Engineering in the Real SOC: A Practitioner’s Guide to the ADE Framework</a> was originally published in <a href="https://medium.com/fmisec">FMI Cyber Security Consulting Services</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building an AI-Powered Android Pentesting Workflow with ARTEMIS and Burpsuite MCP]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/fmisec/building-an-ai-powered-android-pentesting-workflow-with-artemis-and-burp-suite-mcp-591f8fd9a32f?source=rss----5aebc5961dd0---4"><img src="https://cdn-images-1.medium.com/max/1305/1*zZPPf9jKcNeh6dOAlUdxDQ.png" width="1305"></a></p><p class="medium-feed-snippet">Free Link &#x2026;</p><p class="medium-feed-link"><a href="https://medium.com/fmisec/building-an-ai-powered-android-pentesting-workflow-with-artemis-and-burp-suite-mcp-591f8fd9a32f?source=rss----5aebc5961dd0---4">Continue reading on FMI Cyber Security Consulting Services »</a></p></div>]]></description>
            <link>https://medium.com/fmisec/building-an-ai-powered-android-pentesting-workflow-with-artemis-and-burp-suite-mcp-591f8fd9a32f?source=rss----5aebc5961dd0---4</link>
            <guid isPermaLink="false">https://medium.com/p/591f8fd9a32f</guid>
            <category><![CDATA[android-pentesting]]></category>
            <category><![CDATA[red-team]]></category>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[penetration-testing]]></category>
            <category><![CDATA[mobile-pentesting]]></category>
            <dc:creator><![CDATA[Rio [Redacted]]]></dc:creator>
            <pubDate>Tue, 13 Oct 2026 02:01:04 GMT</pubDate>
            <atom:updated>2026-10-14T01:23:00.726Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Hunting on the Wire : A Practical Guide to Network Threat Hunting — Part 1]]></title>
            <link>https://medium.com/fmisec/hunting-on-the-wire-a-practical-guide-to-network-threat-hunting-part-1-be41c6dfeb77?source=rss----5aebc5961dd0---4</link>
            <guid isPermaLink="false">https://medium.com/p/be41c6dfeb77</guid>
            <category><![CDATA[threat-hunting]]></category>
            <category><![CDATA[network-forensics]]></category>
            <category><![CDATA[network-threat-hunting]]></category>
            <category><![CDATA[blue-team]]></category>
            <category><![CDATA[research]]></category>
            <dc:creator><![CDATA[Digit Oktavianto]]></dc:creator>
            <pubDate>Thu, 08 Oct 2026 02:01:03 GMT</pubDate>
            <atom:updated>2026-10-08T02:01:03.596Z</atom:updated>
            <content:encoded><![CDATA[<h3>Network Threat Hunting, Part 1: You Can Hide Processes, But You Can’t Hide Packets</h3><p><em>Why the network is the threat hunter’s ground truth — and how to think about hunting before you touch a single log.</em></p><p><em>This is Part 1 of a three-part series for SOC analysts, threat hunters and DFIR practitioners. Part 1 covers the why and the mindset. Part 2 goes deep on the technical hunts (long connections, lateral movement, beacons, DNS, protocol anomalies, outliers). Part 3 is hands-on: lab setup, tooling, datasets and exercises.</em></p><p>Every incident responder has lived this moment. The EDR console is green. The SIEM has no high-severity alerts. Then a third party calls: your data is on a leak site, or one of your IPs is talking to known infrastructure.</p><p>The numbers say this is still common. Mandiant’s <a href="https://cloud.google.com/blog/topics/threat-intelligence/m-trends-2026">M-Trends 2026</a> reports a global median dwell time of <strong>14 days</strong>, up from 11 days the year before. For cyber-espionage intrusions the median was <strong>122 days</strong>. One espionage campaign (BRICKSTORM) persisted for nearly <strong>400 days</strong> on network appliances — devices that typically run no EDR agent at all. And <strong>48%</strong> of intrusions were still first detected by someone outside the victim organization.</p><p>That gap — between “nothing is alerting” and “we are compromised” — is exactly where threat hunting lives. And in my experience, the network is the best place to start looking.</p><h3>What threat hunting is (and what it isn’t)</h3><p>Threat hunting is the proactive, human-led search for adversary activity that your existing controls did not detect. The key word is <em>proactive</em>: you are not waiting for an alert. You start from the assumption that something may already be inside and go looking for evidence that proves or disproves it.</p><p>Active Countermeasures (ACM), the team behind RITA and AC-Hunter, frames it even more sharply in their <a href="https://www.activecountermeasures.com/wp-content/uploads/2021/02/Network-Threat-Hunting-202102.pdf">Network Threat Hunting training</a>: a hunt is a <strong>proactive validation of every system connected to the network</strong>, executed without assumptions, which produces a <strong>compromise assessment for each device</strong>. In other words, the output of a good hunt is not “we found nothing”. It is “we checked these hosts against these behaviours, and here is the disposition of each one.”</p><p>It helps to separate hunting from its neighbours:</p><ul><li><strong>Detection engineering / SOC monitoring</strong> is reactive and automated. A rule fires, an analyst triages. It only catches what someone already wrote a rule for.</li><li><strong>Incident response</strong> starts once you <em>know</em> there is an incident. Scope is defined by the alert or the report.</li><li><strong>Threat hunting</strong> starts with a question, not an alert. Its outputs feed both of the above: confirmed findings go to IR; repeatable patterns become new detections.</li></ul><p>If your “hunting” is re-running yesterday’s SIEM queries and closing the tickets, you are doing monitoring. That is valuable, but it is not hunting.</p><h3>Why start on the network?</h3><p><strong>ActiveCounterMeasures (ACM)</strong> training uses a line that has stuck with me for years: <strong>“you can hide processes, but you can’t hide packets.”</strong></p><p>On the endpoint the attacker is on home turf. They can unhook EDR, kill or blind agents with vulnerable drivers, clear event logs, run in memory, or simply live on a device that never had an agent — a VPN concentrator, a firewall, a printer, an OT controller, a hypervisor. Logs on a compromised host are evidence <em>the attacker controls</em>.</p><p>The network is different. For an intrusion to be useful, the attacker needs to talk to it:</p><ul><li>They need a <strong>command-and-control (C2) channel</strong> to send instructions and receive results.</li><li>They need to <strong>move laterally</strong> to reach the data or the domain controller or to any crown jewel assets.</li><li>They usually need to <strong>move data out for performing data exfiltration.</strong></li></ul><p>Each of those leaves traffic. A sensor placed off-host, on a SPAN port or TAP, records that traffic independently of the compromised endpoint. The attacker can encrypt the content, but they cannot easily hide <em>that</em> a connection happened, how long it lasted, how often it repeated, how many bytes moved, or which domain was resolved first.</p><p>This is also why ACM argues against starting a hunt from raw syslog. Syslog was never designed as a security data source, message formats vary by vendor, and much log review collapses into signature matching — what they call the “1990s anti-virus model”. Network metadata, by contrast, is uniform: every connection has a source, a destination, a port, a protocol, a duration and a byte count, no matter which OS or app produced it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cNOHsbKzy_ErxkUB0ki__w.png" /><figcaption>Network Threat Hunting</figcaption></figure><h3>The C2 lens: focus on persistence first</h3><p>The most practical idea in ACM’s <a href="https://www.activecountermeasures.com/how-to-threat-hunt-your-network/">How to Threat Hunt Your Network</a> is ordering your work by threat. Instead of trying to look at everything, start with the evidence that most strongly indicates compromise: <strong>persistent outbound communication</strong>.</p><p>An implant has two ways to stay in touch with its operator:</p><ol><li><strong>Long connections</strong> — keep one session open for hours or days and push commands down it.</li><li><strong>Beacons</strong> — open short sessions on a schedule, just long enough to check in (“any tasks for me?”), then disconnect.</li></ol><p>Either way, the pattern is <em>persistent</em>, and persistence is something you can measure from connection metadata alone. ACM’s five-step method builds on that:</p><ol><li>Identify persistent communication channels leaving the network.</li><li>Analyse the protocol being used.</li><li>Evaluate the internal host originating the traffic.</li><li>Check the reputation of the destination.</li><li>Disposition: allow-list as benign, or escalate to incident response.</li></ol><p>We will walk through each of these technically in Part 2.</p><h3>Three (or four) ways to start a hunt</h3><p>NetWitness’ overview of <a href="https://www.netwitness.com/blog/types-of-network-threat-hunting/">network threat hunting types</a> groups hunts into three families. Splunk’s <a href="https://www.splunk.com/en_us/blog/security/peak-threat-hunting-framework.html">PEAK framework</a> adds a fourth. Here is how each looks on the wire:</p><ul><li><strong>Hypothesis-driven.</strong> You start from a testable statement. <em>“If an implant is using HTTPS C2 from our server VLAN, we will see regular outbound TLS sessions to a destination no other server talks to.”</em> Precise and efficient, but only as good as the hypothesis.</li><li><strong>Intelligence-driven.</strong> You start from external knowledge: a vendor report, an ISAC advisory, a CERT bulletin. <em>“Campaign X uses DNS TXT records for tasking and domains registered in the last week.”</em> Great for distributed environments where threats change faster than rules.</li><li><strong>Anomaly / baseline-driven.</strong> You start from what normal looks like and hunt the deviations: the one host talking to a new country, the only user-agent seen once, the server that suddenly uploads 20 GB. This finds novel attacks and insiders, but it needs mature data and analysts who know the environment.</li><li><strong>Model-assisted (M-ATH, from PEAK).</strong> You use statistics or machine learning to score behaviour — for example, a beacon score — and humans investigate the top of the list. RITA’s beacon scoring, which we use in Parts 2 and 3, is a simple version of this.</li></ul><p>In practice, good hunts mix them. A threat-intel report gives you the hypothesis; a baseline tells you what is abnormal; a model ranks the candidates.</p><p>PEAK’s three phases are also a useful wrapper for any hunt: <strong>Prepare</strong> (pick the topic, research, check that the data exists), <strong>Execute</strong> (analyse, pivot, investigate) and <strong>Act</strong> (document, automate the detection, communicate).</p><h3>Where hunting sits on the maturity curve</h3><p>Two classic models help explain why network hunting pays off.</p><p>The <strong>Hunting Maturity Model</strong> (David Bianco, originally at Sqrrl) goes from HMM0 (relies on automated alerting only) to HMM4 (most successful hunts are automated into detections). Most teams I work with sit at HMM1–HMM2: they collect data and can search for IOCs, but rarely create new analytics. Network hunting is one of the fastest ways to climb, because the analytics — duration, frequency, volume, rarity — are simple to express and automate.</p><p>Bianco’s <strong>Pyramid of Pain</strong> explains the other half. Blocking an IP or a hash costs the adversary almost nothing. Detecting their <em>behaviour</em> — how their C2 checks in, how their tunnel abuses DNS — is near the top of the pyramid and is expensive for them to change. Beacon timing and DNS subdomain patterns are behavioural. Rotating infrastructure does not make them go away.</p><h3>What you need to see: network data sources</h3><p>The <a href="https://www.puppygraph.com/blog/network-threat-hunting">PuppyGraph guide to network threat hunting</a> makes a point that is easy to forget: network evidence is only useful when you can connect it to <em>who</em> and <em>what</em>. You need the traffic, plus the context to tie an IP to a host, a user and a process. Here are the main sources and their trade-offs:</p><ul><li><strong>Full packet capture (PCAP)</strong> — tcpdump, Arkime, or a commercial recorder. Maximum fidelity: payloads, file carving, protocol reconstruction. Heavy on storage, so retention is often measured in days. Best for deep-dive validation, not for scanning months of history.</li><li><strong>Network security monitoring logs (Zeek)</strong> — Zeek turns packets into rich, structured logs: conn.log, dns.log, http.log, ssl.log, x509.log, files.log and more. Roughly 1–2% of the size of the PCAP, so you can keep months. This is the workhorse for most of what follows in this series.</li><li><strong>NetFlow / IPFIX / VPC flow logs</strong> — cheap, everywhere, built into routers, switches and cloud platforms. No application layer, and often sampled, so beacon and DNS analysis is limited. Still excellent for volume, rarity and east-west network hunting.</li><li><strong>Firewall and proxy logs</strong> — widely available; useful for egress analysis and URL/user-agent hunting. Quality varies by vendor; some firewalls only log session end.</li><li><strong>DNS logs</strong> — resolver query logs or passive DNS. Critical for DNS-based C2, DGA and newly-registered-domain hunts.</li><li><strong>Identity and asset context</strong> — DHCP leases, VPN sessions, authentication logs, CMDB/asset inventory. Without these, “10.1.4.37” is just a number.</li><li><strong>Endpoint telemetry</strong> — EDR network events or Sysmon Event ID 3 tie a connection to the process that made it. This is your pivot target after the network hunt finds something.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oRAApjVa2LllCo8jzCWkTw.png" /><figcaption>Network Threat Hunting Hypotheses Ideas</figcaption></figure><h3>Sensor placement matters more than tools</h3><p>The best analytics fail if the sensor is in the wrong place. A few rules from the field:</p><ul><li><strong>Capture inside the egress firewall, before NAT.</strong> Otherwise every outbound connection appears to come from the firewall’s public IP and you lose the internal host.</li><li><strong>Capture before the internal DNS resolver forwards queries</strong>, or log on the resolver itself. Otherwise all DNS appears to come from the resolver.</li><li><strong>Do not ignore east-west traffic.</strong> Perimeter sensors see C2 and exfiltration, but not lateral movement between internal segments. Core switch SPANs or virtual TAPs in hypervisors and cloud VPCs close that gap.</li><li><strong>Measure your visibility before you hunt.</strong> Confirm that the sensor actually sees every egress path, including guest Wi-Fi, OT links, cloud egress and direct internet breakouts at branch offices.</li></ul><h3>Know the limits</h3><p>Network hunting is powerful, but it is not magic. Be honest about the blind spots:</p><ul><li><strong>Encryption.</strong> TLS 1.3, encrypted ClientHello (ECH) and DNS over HTTPS (DoH) hide content and, increasingly, destination names. <strong>You will rely more on metadata: timing, size, certificates, and fingerprints such as </strong><a href="https://github.com/FoxIO-LLC/ja4"><strong>JA4+</strong></a><strong>.</strong></li><li><strong>Volume.</strong> Unbounded searches across terabytes overwhelm analysts. Scope every hunt.</li><li><strong>Identity ambiguity.</strong> DHCP churn, NAT, shared proxies and cloud IPs that change owners make attribution harder.</li><li><strong>Noisy baselines.</strong> Software updaters, telemetry agents, chat clients and monitoring tools all beacon. Expect false positives and build an allow-list as you go.</li></ul><h3>What a successful hunt produces</h3><p>A hunt that “found nothing” is still a success if it produces outputs. At the end of every hunt you should have:</p><ol><li><strong>A disposition for every host in scope</strong> — benign, suspicious (needs more data), or compromised (escalated to IR).</li><li><strong>An updated allow-list</strong> of known-good long connections and beacons, with the business reason recorded.</li><li><strong>New or improved detections</strong> for anything that can be automated.</li><li><strong>A list of visibility gaps</strong> discovered along the way (missing logs, unmonitored network segments).</li><li><strong>Documentation</strong> of scope, data, queries and findings, so the hunt can be repeated.</li></ol><p>Measure the hunting program by those outputs — time-to-disposition, detections created, gaps closed — not by the number of hunts run.</p><h3>Coming up in Part 2</h3><p>In Part 2 we get our hands dirty. We will hunt C2 the way an analyst actually does it: finding long connections and beacons in Zeek conn.log, catching DNS tunnels by counting unique subdomains, spotting outliers in bytes, user-agents and certificates, and scoring each finding so you can make a defensible call.</p><h3>References and further reading</h3><ul><li>Active Countermeasures — <a href="https://www.activecountermeasures.com/how-to-threat-hunt-your-network/">How to Threat Hunt Your Network</a></li><li>Active Countermeasures — <a href="https://www.activecountermeasures.com/wp-content/uploads/2021/02/Network-Threat-Hunting-202102.pdf">Network Threat Hunting training slides (PDF)</a></li><li>PuppyGraph — <a href="https://www.puppygraph.com/blog/network-threat-hunting">Network Threat Hunting</a></li><li>NetWitness — <a href="https://www.netwitness.com/blog/types-of-network-threat-hunting/">Types of Network Threat Hunting</a></li><li>Splunk SURGe — <a href="https://www.splunk.com/en_us/blog/security/peak-threat-hunting-framework.html">The PEAK Threat Hunting Framework</a></li><li>Mandiant — <a href="https://cloud.google.com/blog/topics/threat-intelligence/m-trends-2026">M-Trends 2026</a></li><li>SANS — <a href="https://www.sans.org/cyber-security-courses/advanced-network-forensics-threat-hunting-incident-response">FOR572: Advanced Network Forensics: Threat Hunting, Analysis, and Incident Response</a></li></ul><p>Long Live Cyber Defenders!!!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=be41c6dfeb77" width="1" height="1" alt=""><hr><p><a href="https://medium.com/fmisec/hunting-on-the-wire-a-practical-guide-to-network-threat-hunting-part-1-be41c6dfeb77">Hunting on the Wire : A Practical Guide to Network Threat Hunting — Part 1</a> was originally published in <a href="https://medium.com/fmisec">FMI Cyber Security Consulting Services</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[IoT Security: Securing File Transfers Over Untrusted Channels]]></title>
            <link>https://medium.com/fmisec/iot-security-securing-file-transfers-over-untrusted-channels-b5daabe66713?source=rss----5aebc5961dd0---4</link>
            <guid isPermaLink="false">https://medium.com/p/b5daabe66713</guid>
            <category><![CDATA[iot-security]]></category>
            <category><![CDATA[iot]]></category>
            <category><![CDATA[research]]></category>
            <category><![CDATA[cybersecurity]]></category>
            <category><![CDATA[cryptography]]></category>
            <dc:creator><![CDATA[Farhan Nurohman]]></dc:creator>
            <pubDate>Wed, 07 Oct 2026 02:01:02 GMT</pubDate>
            <atom:updated>2026-10-07T02:01:02.511Z</atom:updated>
            <content:encoded><![CDATA[<p><em>I implemented Ascon (the NIST lightweight cryptography standard) and X25519 key exchange on a 240 MHz microcontroller, then measured the performance. Here are the figures and the takeaways for anyone building a file transfer protocol.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Lh1rAsv6nUoD6kl2ppkZwA.png" /></figure><h4><strong>Why File Transfer?</strong></h4><p>Between 2020 and 2025, the same extortion group — Cl0p — executed an identical playbook against four consecutive managed file transfer products: Accellion FTA, Fortra GoAnywhere, Progress MOVEit, and the Cleo product suite. The 2023 MOVEit campaign alone impacted over 2,700 organizations and more than 90 million individuals. Exploitation of the Cleo flaw was still active as late as February 2025.</p><p>Note what didn’t happen there. No AES was cracked. No elliptic curve collapsed. What was exploited was SQL injection, authentication bypass, and remote code execution in software sitting at the perimeter — software that held the organizations’ most sensitive data.</p><p>This is the first lesson, and it’s worth stating up front so it doesn’t get lost: <strong>the strength of your cipher is the easiest part of the problem.</strong> The hard parts are protocol design, key handling, and verification discipline.</p><p>But there’s another side of the spectrum that’s rarely discussed. At the opposite end from enterprise-grade MFT servers lies a far more resource-constrained class of data transfer: small devices sending data over narrow channels with no supporting infrastructure at all. No TLS, no certificate authority, no internet connection. There, data transfer often happens <strong>with no encryption whatsoever</strong> — not because nobody cares, but because of the prevailing assumption that cryptography is too expensive for devices that small.</p><h4>Four Questions, One Primitive.</h4><p>Every file transfer protocol — from SCP to S3 multipart uploads — must address four aspects:</p><ul><li><strong>Confidentiality</strong> — can an intermediary read the contents? This is the one everyone remembers.</li><li><strong>Integrity</strong> — large files are split into chunks. If an attacker swaps chunk five with one from another file, reorders chunks, or truncates the transfer mid-way while claiming it’s complete, the received file is corrupted without anyone noticing. This one gets overlooked.</li><li><strong>Authenticity</strong>— a valid chunk that’s recorded and replayed remains cryptographically valid unless a sequence number is bound into the authentication.</li><li><strong>Forward secrecy</strong> — your keys will eventually leak. The question is how much becomes exposed when they do. With one static key for the system’s whole lifetime, the answer is: everything, including traffic an attacker recorded three years ago.</li></ul><p><strong>AEAD </strong>addresses the first three all at once:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/521/1*bblV5Ik2URCUeCW2R2nxXA.png" /></figure><p>A stands for associated data: it is unencrypted but is also authenticated. This is where the chunk number, the last chunk marker, and the sender’s ID are placed — plaintext, but cannot be modified without causing the tag verification to fail.</p><p>Proper AEAD returns a <strong>failure</strong>, not garbage plaintext, when the tag doesn’t match. This isn’t an academic detail: when the Ascon-AEAD128 implementation was submitted to OpenSSL, one review finding was that decryption released the plaintext <em>before</em> tag verification completed. Bugs of that class still happen in the most heavily audited project in the world.</p><h4>Ascon &amp; X25519</h4><p>On August 13, 2025, NIST finalized <strong>SP 800–232</strong>, a lightweight cryptography standard built on the Ascon family — including Ascon-AEAD128, Ascon-Hash256, and its XOF variant. Ascon had previously won as the primary lightweight-encryption choice in the CAESAR competition.</p><p>What makes it interesting: a sponge/duplex construction with a 320-bit state, no key schedule to expand and store, and no S-box lookup tables in memory — so no cache-timing leakage from lookup tables either. A single permutation handles encryption, hashing, and key derivation, so an entire protocol can be built from one primitive: Ascon-XOF works as a KDF out of the box, no need to import HKDF-SHA256 separately. Library support is moving: wolfSSL added it in April 2025, with OpenSSL support following.</p><blockquote>⚠️ <strong>Ascon v1.2 ≠ SP 800–232.</strong> The final standard changes the initial value, adjusts to little-endian formatting for microcontroller performance, and adds options such as nonce masking. The test vectors differ. I implemented v1.2; if you need interoperability today, target SP 800–232.</blockquote><p>For key exchange, X25519 beats NIST P-256 on all three metrics that matter on constrained devices: 523 ms, 47 KB, 55 mW, versus P-256’s 1,582 ms, 64 KB, 103 mW (Tanksale 2024, 48 MHz MCU).</p><p>But more important than the choice of curve is its <strong>ephemerality</strong>. A key pair generated fresh for each session and discarded afterward means a leak today doesn’t unlock yesterday’s recordings. The age file encryption format does exactly this: for every single file, a new ephemeral X25519 key is generated and run through ECDH against the recipient&#39;s public key to wrap the file key.</p><p>One caveat: raw ECDH is vulnerable to man-in-the-middle (MitM) attacks. Without authentication, an attacker can swap both public keys, establish two separate shared secrets, and read everything in between. <strong>ECDH provides confidentiality, not identity.</strong></p><h4>The Design</h4><p>Two layers of keys. A static pre-shared key (PSK), provisioned onto both devices in advance, is used <strong>only</strong> to authenticate the handshake — never to encrypt data. A session key derived via ECDH handles the actual data chunks. This separation is what makes the scheme work: the PSK isn’t repeatedly exposed with every chunk, and a leaked session key doesn’t compromise any other session, past or future.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UjnhN6e7WTZmiA-dI18uGw.png" /></figure><p>Four decisions worth highlighting:</p><ul><li><strong>The ephemeral public key is sent encrypted and authenticated under the PSK.</strong> This is what closes the MitM hole — an attacker without the PSK can’t inject their own public key, since the handshake packet would fail tag verification.</li><li><strong>The counter doubles as the nonce and lives in the associated data.</strong> The receiver reconstructs the nonce without it ever being sent in full, and because the counter is authenticated, it can’t be forged for a replay.</li><li><strong>Verification order: counter first, tag second.</strong> Stale packets are rejected with zero cryptographic operations.</li><li><strong>Ephemeral private keys and shared secrets are explicitly zeroed</strong> immediately after the session key is derived. Without this step, forward secrecy is a claim on paper, not in practice.</li></ul><blockquote>Data frame: ID 2 · counter 8 · timestamp 4 · ciphertext 8 · tag 16 = 38 bytes. Handshake frame: ID 2 · nonce 8 · pubkey 32 · tag 16 = 58 bytes.</blockquote><p>For an 8-byte chunk, the cryptographic overhead is nearly four times the size of the data — the worst-case scenario that could be designed. For a 64 KiB chunk, that same overhead becomes 0.05%.</p><h4>The Result</h4><p>Before any measurement was taken: X25519 was checked against the RFC 7748 test vectors; Ascon-128 against the reference implementation, including 400 random combinations of AD length and plaintext length; plus negative tests with a corrupted tag, corrupted ciphertext, and corrupted AD. A crypto implementation that runs without errors doesn’t mean it’s correct — broken encryption still produces convincing-looking random bytes.</p><blockquote>Hardware: ESP32 at 240 MHz, LoRa SX1276, SF7/BW125. The four schemes were run round-robin so environmental drift would be spread evenly across all of them.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/610/1*SCiqVvWqCnfcCtoV7CkA4g.png" /></figure><p><strong>ECDH is practically free.</strong> The difference between the second and third rows is 230 cycles and 0.14 KB of RAM, in exchange for forward secrecy and MitM resistance. The handshake itself takes 342.6 ms, but since it happens only once every 100 chunks, it amortizes to 3.43 ms per chunk — <strong>0.17%</strong> of the send interval.</p><p><strong>Ascon needs about 35% fewer cycles than AES-GCM for decryption</strong> (25,273 vs. 38,631 — equivalently, AES-GCM takes roughly 53% more cycles than Ascon), even though the ESP32 has an AES hardware accelerator. The reason: the accelerator handles the AES block cipher, but GHASH still runs in software, and on an 8-byte payload, the GHASH overhead outweighs whatever the accelerator saves.</p><blockquote>This finding <strong>reverses</strong>, though, on 64 KiB chunks on a CPU with AES-NI. AES-GCM’s per-byte cost drops sharply, while Ascon stays bound to a fixed number of permutation rounds per 8-byte block. Ascon’s advantage is an advantage <strong>on constrained devices with short messages</strong>, not a universal one.</blockquote><p>This is why age chose ChaCha20-Poly1305 over AES-GCM: not because AES is weak, but because AES is hard to make fast <em>and</em> safe without hardware support. Pick a primitive based on the platform and message size, not on whichever was standardized most recently.</p><p>Five attack scenarios were run against separate attacker nodes. Ascon+ECDH withstood eavesdropping, injection, replay, and MitM-on-handshake. The static-PSK scheme withstood eavesdropping, injection, and replay too — MitM-on-handshake doesn’t apply to it, since it has no ECDH handshake to attack — but it failed the one test that actually matters for key compromise: forward secrecy. I recorded three key-rotation periods, leaked the second period’s session key, and tried decrypting the other two. With static PSK, all three periods were exposed. With Ascon+ECDH, periods 1 and 3 both <strong>failed tag verification</strong> — rejecting the packets outright rather than handing the application incorrect plaintext.</p><p>Now for the part that usually doesn’t make it into articles like this. I flooded the channel with fake chunks:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/492/1*9k19ye-vToXzi8E1aZvjLw.png" /></figure><p>The cryptographic side works perfectly. Zero forged packets got through, and verification latency stayed flat regardless of attack rate. <strong>And availability still dropped to 20%.</strong></p><p>Confidentiality, integrity, and authenticity are three properties. Availability is a fourth, and cryptography doesn’t provide it — that’s handled elsewhere, with rate limiting, channel scheduling, and per-identity quotas. Tie that back to MOVEit: those organizations used TLS; their data was encrypted in transit. What failed was authorization and input validation sitting on top of the cryptography.</p><h4>Limitations</h4><p>Test conditions were controlled: two nodes, 1-meter distance, line-of-sight. The payload was a fixed 8 bytes, so Ascon’s partial-block code path never executes during application use (though it was verified separately via multi-block test vectors). Performance figures don’t generalize across platforms — 24,624 cycles on the Xtensa LX6 doesn’t map directly onto Cortex-M or x86–64. This is an Ascon v1.2 implementation, not SP 800–232. Side-channel analysis is out of scope; any crypto implementation needs an independent audit before production use. And as shown above, availability under flooding was not addressed by the cryptographic design at all.</p><h4>Conclusion</h4><p>Lightweight cryptography is mature enough for serious use now. Ascon has a NIST standard behind it, and on small devices with short messages it beats AES-GCM by a wide margin. Ephemeral key exchange is nearly free — systems still running on a static key for the device’s entire lifetime are carrying technical debt that will eventually come due. Framing details decide everything: chunk numbers and end-markers bound into the associated data are the difference between a secure transfer and an encrypted one that can still be truncated or scrambled. And correct cryptography is a prerequisite, not a guarantee — all four MFT products breached over those four years used sound encryption. The failure was never in the cipher.</p><p>Reference: <a href="https://repository.ipb.ac.id/handle/123456789/178628">Implementation of Lightweight Cryptography Ascon and Elliptic Curve Diffie-Hellman (ECDH) on LoRa Data Transmission Mechanism</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b5daabe66713" width="1" height="1" alt=""><hr><p><a href="https://medium.com/fmisec/iot-security-securing-file-transfers-over-untrusted-channels-b5daabe66713">IoT Security: Securing File Transfers Over Untrusted Channels</a> was originally published in <a href="https://medium.com/fmisec">FMI Cyber Security Consulting Services</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>