<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Stephen's blog</title>
        <link>https://ics.red</link>
        <description>Cybersecurity, OT security, and homelab adventures</description>
        <lastBuildDate>Thu, 15 Oct 2026 09:25:42 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <copyright>All rights reserved 2026</copyright>
        <item>
            <title><![CDATA[Highway to Critical Heaven: Scaling Vulnerability Discovery Across Hundreds of Routers]]></title>
            <link>https://ics.red/posts/highway-to-critical-heaven</link>
            <guid isPermaLink="false">https://ics.red/posts/highway-to-critical-heaven</guid>
            <pubDate>Thu, 15 Oct 2026 05:43:54 GMT</pubDate>
            <description><![CDATA[My BSides Atlanta 2026 talk with Vipin B.: collecting and auditing firmware for hundreds of routers with our own tooling and AI agents, and the 40 critical findings that came out of it. Full slide deck embedded.]]></description>
            <content:encoded><![CDATA[<p><em>Presented at <strong>BSides Atlanta 2026</strong> — Atlanta, GA. Co-presented with Vipin B. of <a href="https://vert3x.io">vert3x</a>.</em></p>
<p>These are the slides from our talk on Project CritHeav3n: pulling firmware for hundreds of consumer routers, auditing it at scale with a mix of our own tooling and AI agents, and checking which findings actually hold up on real hardware.</p>
<div style="position:relative;width:100%;height:0;padding-top:calc(56.25% + 46px);margin:1.5rem 0;border:1px solid var(--card-separator-color);border-radius:8px;overflow:hidden;background:#050506">
<iframe src="/talks/critical-heaven/index.html" title="Highway to Critical Heaven — slides" allowfullscreen loading="lazy" style="position:absolute;inset:0;width:100%;height:100%;border:0"></iframe>
</div>
<p style="text-align:center;margin:0 0 1.5rem"><a href="https://vert3x.io/slides/bsides.pdf" target="_blank" rel="noopener" style="display:inline-block;padding:10px 24px;border-radius:8px;background:var(--accent-color);color:var(--accent-color-text);font-weight:600;text-decoration:none">Open the slides (PDF) on vert3x.io</a></p>
<p>Navigate with <strong>← / →</strong> (or swipe on a phone); press <strong>R</strong> to jump back to the first slide. More of our work is at <a href="https://vert3x.io">vert3x.io</a>.</p>
<h2 id="what-it-covers">What it covers<a class="heading-anchor" href="#what-it-covers" aria-label="Copy link to “What it covers”">#</a></h2>
<ul>
<li><strong>Getting the firmware:</strong> open vendor FTP portals and storage buckets, plus browser agents that register accounts and download from bot-hardened support pages.</li>
<li><strong>Post-processing:</strong> recovering decryption keys (older images, side channels, JTAG, flash dumps) and cataloging every image by product family and release date.</li>
<li><strong>Two pipelines, side by side:</strong> the top 50 products per vendor were split between Axiom, our own unpack, points-of-interest, deep-scan and reproduce pipeline (91% of its findings were valid vulnerabilities), and a Codex audit with one orchestrator and five parallel auditors running two waves over 23 hours.</li>
<li><strong>Case studies:</strong> a MikroTik SNMP stack overflow (CVE-2026-88464, now patched, with 44,531 MikroTik SNMP hosts publicly reachable per Censys), and a TP-Link mesh "RCE" that scored 8.8 in emulation but never reaches the daemon on a real network, because the kernel reads EtherType 0x0300 as an 802.3 length.</li>
<li><strong>Results:</strong> 40 critical findings across MikroTik, NETGEAR, D-Link, GL.iNet, ipTIME, ASUS, Xiaomi and TP-Link, 19 of them with confirmed code execution. Command injection was the most common weakness by a wide margin.</li>
</ul>
<h2 id="the-takeaway">The takeaway<a class="heading-anchor" href="#the-takeaway" aria-label="Copy link to “The takeaway”">#</a></h2>
<p>Command injection should be dead in 2026, and it still turned up in 19 of our findings. Vendors could cut most of the damage by sanitizing input, running network daemons without root (10 of 16 finding families ended in code execution), and patching a shared component across every product line that ships it, not just the model that was reported. On cost, the whole project ran on about $800 of Codex subscriptions, against roughly $10,000 at API prices.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Ground Control to Major Tom: Eavesdropping on Orbit]]></title>
            <link>https://ics.red/posts/ground-control-to-major-tom</link>
            <guid isPermaLink="false">https://ics.red/posts/ground-control-to-major-tom</guid>
            <pubDate>Sun, 07 Jun 2026 07:05:00 GMT</pubDate>
            <description><![CDATA[My HackSpaceCon 2026 talk on the (in)security of the protocols holding low-earth orbit together  AX.25, CCSDS, and DVB-S2. Full slide deck embedded.]]></description>
            <content:encoded><![CDATA[<p><em>Presented at <strong>HackSpaceCon 2026</strong> — Cocoa Beach, FL.</em></p>
<p>Unfortunately the recording equipment wasn't working, so there's no video — but here are the slides. This talk breaks down three of the protocols holding low-earth orbit together — <strong>AX.25</strong>, <strong>CCSDS</strong>, and <strong>DVB-S/S2</strong> — and shows how little stands between an antenna and the data.</p>
<div style="position:relative;width:100%;aspect-ratio:16/9;margin:1.5rem 0;border:1px solid var(--card-separator-color);border-radius:8px;overflow:hidden;background:#050506">
<iframe src="/talks/major-tom/index.html" title="Ground Control to Major Tom — slides" allowfullscreen loading="lazy" style="position:absolute;inset:0;width:100%;height:100%;border:0"></iframe>
</div>
<p style="text-align:center;margin:0 0 1.5rem"><a href="/talks/major-tom/index.html" target="_blank" rel="noopener" style="display:inline-block;padding:10px 24px;border-radius:8px;background:var(--accent-color);color:var(--accent-color-text);font-weight:600;text-decoration:none">▶ Open the slides fullscreen</a></p>
<p>Navigate with <strong>← / →</strong> (or the on-screen arrows); press <strong>R</strong> to jump back to the first slide.</p>
<h2 id="what-it-covers">What it covers<a class="heading-anchor" href="#what-it-covers" aria-label="Copy link to “What it covers”">#</a></h2>
<ul>
<li><strong>AX.25</strong> — the amateur backbone flying on the ISS and hundreds of CubeSats, with <em>zero</em> cryptographic defenses (partly by regulation).</li>
<li><strong>CCSDS</strong> — the professional stack (NASA / ESA / JAXA). A security overlay, SDLS, exists — but adoption is glacial and most missions still ship without it.</li>
<li><strong>DVB-S / S2</strong> — the commercial workhorse behind satellite TV, VSAT and backhaul. Researchers intercepted live traffic with ~€300 of consumer gear.</li>
<li><strong>Viasat / KA-SAT</strong> — proof this isn't theoretical: tens of thousands of modems knocked offline through a ground-segment compromise on the first morning of the 2022 invasion of Ukraine.</li>
</ul>
<h2 id="the-takeaway">The takeaway<a class="heading-anchor" href="#the-takeaway" aria-label="Copy link to “The takeaway”">#</a></h2>
<p>Passive reception has become cheap for whole classes of satellite traffic, active RF attacks are harder (and often illegal), and <strong>ground-segment compromise remains the most realistic high-impact path</strong>. The ground is in 2026; orbit is still in 1996.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Spectral DNA]]></title>
            <link>https://ics.red/posts/spectraldna</link>
            <guid isPermaLink="false">https://ics.red/posts/spectraldna</guid>
            <pubDate>Wed, 06 May 2026 19:21:00 GMT</pubDate>
            <description><![CDATA[Spectral DNA research poster.]]></description>
            <content:encoded><![CDATA[<img src="/writeups/spectraldna/spectral-dna-poster-1600.webp" srcset="/writeups/spectraldna/spectral-dna-poster-800.webp 800w, /writeups/spectraldna/spectral-dna-poster-1600.webp 1600w, /writeups/spectraldna/spectral-dna-poster-2400.webp 2400w" sizes="(min-width: 1400px) 706px, (min-width: 800px) 62vw, 100vw" alt="Spectral DNA research poster" decoding="async" fetchpriority="high" width="1600" height="2099">
<p>Open the <a href="https://media.ics.red/uploads/a7399888-2053-4d31-897b-b0ebb3699896.png">full-resolution poster</a> (PNG, 4.4 MB) or the <a href="https://media.ics.red/uploads/755d84e9-ff50-4431-ae01-5c79ed8971a6.pdf">PDF</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Advent of Code 2025: Solving Puzzles with Industrial Ladder Logic]]></title>
            <link>https://ics.red/posts/advent-of-code-2025-ladder-logic</link>
            <guid isPermaLink="false">https://ics.red/posts/advent-of-code-2025-ladder-logic</guid>
            <pubDate>Tue, 02 Dec 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Taking an unconventional approach to Advent of Code by solving puzzles using Allen-Bradley Micro870 PLCs and ladder logic programming]]></description>
            <content:encoded><![CDATA[<h2 id="the-challenge-advent-of-code-meets-industrial-automation">The Challenge: Advent of Code Meets Industrial Automation<a class="heading-anchor" href="#the-challenge-advent-of-code-meets-industrial-automation" aria-label="Copy link to “The Challenge: Advent of Code Meets Industrial Automation”">#</a></h2>
<p>What happens when you combine <a href="https://adventofcode.com/">Advent of Code</a> with industrial control systems? Ladder logic solutions running on actual PLCs.</p>
<p>This year, I solved Advent of Code 2025 puzzles using <strong>ladder logic on an Allen-Bradley Micro870 PLC</strong>, with a FactoryTalk Optix HMI for visualization.</p>
<p>Github Repo with Code: <a href="https://github.com/ladderlogix/Advent-of-Code-2025">https://github.com/ladderlogix/Advent-of-Code-2025</a></p>
<hr>
<h2 id="hardware-setup">Hardware Setup<a class="heading-anchor" href="#hardware-setup" aria-label="Copy link to “Hardware Setup”">#</a></h2>





















<div class="table-wrap" tabindex="0" role="region" aria-label="Table"><table><thead><tr><th>Component</th><th>Model</th></tr></thead><tbody><tr><td><strong>PLC</strong></td><td>Allen-Bradley Micro870 (2080-L70E-24QBBN)</td></tr><tr><td><strong>HMI</strong></td><td>FactoryTalk Optix</td></tr><tr><td><strong>Communication</strong></td><td>Ethernet/IP</td></tr></tbody></table></div>
<hr>
<h2 id="day-1-secret-entrance">Day 1: Secret Entrance<a class="heading-anchor" href="#day-1-secret-entrance" aria-label="Copy link to “Day 1: Secret Entrance”">#</a></h2>
<h3 id="the-problem">The Problem<a class="heading-anchor" href="#the-problem" aria-label="Copy link to “The Problem”">#</a></h3>
<p>You arrive at the secret entrance to the North Pole base, but the password has changed. A safe with a dial (numbers 0-99) holds the answer. Your puzzle input contains rotation instructions like <code>L68</code> (left 68 clicks) or <code>R48</code> (right 48 clicks).</p>
<p>The dial starts at <strong>50</strong> and wraps around (left from 0 goes to 99, right from 99 goes to 0).</p>
<p><strong>Part 1</strong>: Count how many times the dial <strong>ends</strong> on position 0 after a rotation.</p>
<p><strong>Part 2</strong>: Count how many times the dial <strong>passes through or lands on</strong> position 0 during ALL rotations (including mid-rotation).</p>
<hr>
<h2 id="video-demonstrations">Video Demonstrations<a class="heading-anchor" href="#video-demonstrations" aria-label="Copy link to “Video Demonstrations”">#</a></h2>
<h3 id="day-1-part-1">Day 1 Part 1<a class="heading-anchor" href="#day-1-part-1" aria-label="Copy link to “Day 1 Part 1”">#</a></h3>
<iframe width="100%" height="400" src="https://www.youtube.com/embed/exmJLdwC1RU" frameborder="0" allowfullscreen loading="lazy" title="Embedded content"></iframe>
<h3 id="day-1-part-2">Day 1 Part 2<a class="heading-anchor" href="#day-1-part-2" aria-label="Copy link to “Day 1 Part 2”">#</a></h3>
<iframe width="100%" height="400" src="https://www.youtube.com/embed/s28MHhT4mHE" frameborder="0" allowfullscreen loading="lazy" title="Embedded content"></iframe>
<hr>
<h2 id="part-1-solution">Part 1 Solution<a class="heading-anchor" href="#part-1-solution" aria-label="Copy link to “Part 1 Solution”">#</a></h2>
<p>The algorithm processes 4,146 rotation instructions, tracking position on a circular 0-99 dial.</p>
<h3 id="rung-0---reset">Rung 0 - Reset<a class="heading-anchor" href="#rung-0---reset" aria-label="Copy link to “Rung 0 - Reset”">#</a></h3>
<p><img src="https://media.ics.red/images/aoc/Rung1.png" alt="Rung 1" decoding="async" loading="lazy" width="1261" height="196"></p>
<h3 id="rung-1---start-processing">Rung 1 - Start Processing<a class="heading-anchor" href="#rung-1---start-processing" aria-label="Copy link to “Rung 1 - Start Processing”">#</a></h3>
<p><img src="https://media.ics.red/images/aoc/Rung2.png" alt="Rung 2" decoding="async" loading="lazy" width="711" height="446"></p>
<h3 id="rung-2---check-if-complete">Rung 2 - Check If Complete<a class="heading-anchor" href="#rung-2---check-if-complete" aria-label="Copy link to “Rung 2 - Check If Complete”">#</a></h3>
<p><img src="https://media.ics.red/images/aoc/Rung3.png" alt="Rung 3" decoding="async" loading="lazy" width="601" height="196"></p>
<h3 id="rung-3---get-direction-from-array">Rung 3 - Get Direction From Array<a class="heading-anchor" href="#rung-3---get-direction-from-array" aria-label="Copy link to “Rung 3 - Get Direction From Array”">#</a></h3>
<p><img src="https://media.ics.red/images/aoc/Rung4.png" alt="Rung 4" decoding="async" loading="lazy" width="601" height="396"></p>
<h3 id="rung-4---get-distance-from-array">Rung 4 - Get Distance From Array<a class="heading-anchor" href="#rung-4---get-distance-from-array" aria-label="Copy link to “Rung 4 - Get Distance From Array”">#</a></h3>
<p><img src="https://media.ics.red/images/aoc/Rung5.png" alt="Rung 5" decoding="async" loading="lazy" width="601" height="396"></p>
<h3 id="rung-5---subtract-direction--left">Rung 5 - Subtract (Direction = Left)<a class="heading-anchor" href="#rung-5---subtract-direction--left" aria-label="Copy link to “Rung 5 - Subtract (Direction = Left)”">#</a></h3>
<p><img src="https://media.ics.red/images/aoc/Rung6.png" alt="Rung 6" decoding="async" loading="lazy" width="711" height="196"></p>
<h3 id="rung-6---modulo-wraparound">Rung 6 - Modulo Wraparound<a class="heading-anchor" href="#rung-6---modulo-wraparound" aria-label="Copy link to “Rung 6 - Modulo Wraparound”">#</a></h3>
<p><img src="https://media.ics.red/images/aoc/Rung7.png" alt="Rung 7" decoding="async" loading="lazy" width="711" height="196"></p>
<h3 id="rung-7---handle-negative-values">Rung 7 - Handle Negative Values<a class="heading-anchor" href="#rung-7---handle-negative-values" aria-label="Copy link to “Rung 7 - Handle Negative Values”">#</a></h3>
<p><img src="https://media.ics.red/images/aoc/Rung8.png" alt="Rung 8" decoding="async" loading="lazy" width="601" height="196"></p>
<h3 id="rung-8---count-zeros">Rung 8 - Count Zeros<a class="heading-anchor" href="#rung-8---count-zeros" aria-label="Copy link to “Rung 8 - Count Zeros”">#</a></h3>
<p><img src="https://media.ics.red/images/aoc/Rung9.png" alt="Rung 9" decoding="async" loading="lazy" width="931" height="196"></p>
<h3 id="rung-9---advance-step">Rung 9 - Advance Step<a class="heading-anchor" href="#rung-9---advance-step" aria-label="Copy link to “Rung 9 - Advance Step”">#</a></h3>
<p><img src="https://media.ics.red/images/aoc/Rung10.png" alt="Rung 10" decoding="async" loading="lazy" width="931" height="196"></p>
<hr>
<h2 id="part-2-solution">Part 2 Solution<a class="heading-anchor" href="#part-2-solution" aria-label="Copy link to “Part 2 Solution”">#</a></h2>
<p>Part 2 requires counting every time the dial crosses position 0, not just when it lands there. This adds significant complexity with 6 different cases to handle.</p>
<h3 id="rung-10---zero-crossing-calculation">Rung 10 - Zero Crossing Calculation<a class="heading-anchor" href="#rung-10---zero-crossing-calculation" aria-label="Copy link to “Rung 10 - Zero Crossing Calculation”">#</a></h3>
<p>This massive branching rung handles all the edge cases for counting zero crossings:</p>
<p><img src="https://media.ics.red/images/aoc/Rung11.png" alt="Rung 11" decoding="async" loading="lazy" width="601" height="196"></p>
<h3 id="rung-11---part-2-processing">Rung 11 - Part 2 Processing<a class="heading-anchor" href="#rung-11---part-2-processing" aria-label="Copy link to “Rung 11 - Part 2 Processing”">#</a></h3>
<p><img src="https://media.ics.red/images/aoc/part2/Rung12.png" alt="Rung 12" decoding="async" loading="lazy" width="601" height="196"></p>
<h3 id="rung-12---part-2-advance">Rung 12 - Part 2 Advance<a class="heading-anchor" href="#rung-12---part-2-advance" aria-label="Copy link to “Rung 12 - Part 2 Advance”">#</a></h3>
<p><img src="https://media.ics.red/images/aoc/part2/Rung13.png" alt="Rung 13" decoding="async" loading="lazy" width="601" height="196"></p>
<hr>
<p><em>Stay tuned for more days of Advent of Code solved with ladder logic!</em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Vulnerabilities in Cisco Stratix 5400: RCE Attack Chain]]></title>
            <link>https://ics.red/posts/stratix-5400-research</link>
            <guid isPermaLink="false">https://ics.red/posts/stratix-5400-research</guid>
            <pubDate>Sat, 22 Nov 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Security assessment uncovering GET-based authenticated RCE with no CSRF protection, password hash exposure, and privilege escalation in industrial Ethernet switches]]></description>
            <content:encoded><![CDATA[<h2 id="executive-summary">Executive Summary<a class="heading-anchor" href="#executive-summary" aria-label="Copy link to “Executive Summary”">#</a></h2>
<p>While playing around with a Rockwell Stratix 5400 industrial Ethernet switch, I uncovered a series of <strong>vulnerabilities</strong> that, when chained together, allow complete device compromise with minimal effort.</p>
<h3 id="key-findings">Key Findings<a class="heading-anchor" href="#key-findings" aria-label="Copy link to “Key Findings”">#</a></h3>






























<div class="table-wrap" tabindex="0" role="region" aria-label="Table"><table><thead><tr><th>Vulnerability</th><th>Impact</th><th>Exploitability</th></tr></thead><tbody><tr><td><strong>GET-Based RCE (No CSRF)</strong></td><td>Full command execution</td><td>Trivial - single HTTP GET</td></tr><tr><td><strong>CSRF to RCE</strong></td><td>Unauthenticated to RCE via CSRF</td><td>Easy - malicious webpage</td></tr><tr><td><strong>Password Hash Exposure</strong></td><td>Complete credential disclosure</td><td>Trivial - view HTML source</td></tr><tr><td><strong>Privilege Escalation Chain</strong></td><td>Read-only to Admin escalation</td><td>Easy - hash cracking</td></tr></tbody></table></div>
<p><img src="https://media.ics.red/images/stratix/switchinsetup.jpg" alt="Cisco Stratix 5400 Switch" decoding="async" loading="lazy" width="1200" height="1600"></p>
<p><em>Cisco Stratix 5400 Industrial Ethernet Switch - commonly deployed in critical infrastructure</em></p>
<h2 id="background-the-cisco-stratix-5400">Background: The Cisco Stratix 5400<a class="heading-anchor" href="#background-the-cisco-stratix-5400" aria-label="Copy link to “Background: The Cisco Stratix 5400”">#</a></h2>
<p>The Cisco Stratix 5400 is an <strong>industrial-grade Ethernet switch</strong> designed for harsh environments including manufacturing facilities, utilities, oil &#x26; gas operations, and transportation systems.</p>
<p><strong>Target Device Specifications:</strong></p>
<ul>
<li><strong>Model:</strong> 1783-BMS10CGP</li>
</ul>
<hr>
<h2 id="finding-1-get-based-remote-code-execution-no-csrf-protection">Finding #1: GET-Based Remote Code Execution (No CSRF Protection)<a class="heading-anchor" href="#finding-1-get-based-remote-code-execution-no-csrf-protection" aria-label="Copy link to “Finding #1: GET-Based Remote Code Execution (No CSRF Protection)”">#</a></h2>
<h3 id="the-vulnerability">The Vulnerability<a class="heading-anchor" href="#the-vulnerability" aria-label="Copy link to “The Vulnerability”">#</a></h3>
<p>The web interface exposes a <strong>GET-based command execution endpoint</strong> that accepts arbitrary CLI commands <strong>without CSRF token protection</strong>:</p>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code>Endpoint: /%24{variable}%0A?{variable}=&#x3C;command>
Method: GET
Authentication: Required (HTTP Basic Auth)
CSRF Protection: NONE
</code></pre></div>
<h3 id="technical-details">Technical Details<a class="heading-anchor" href="#technical-details" aria-label="Copy link to “Technical Details”">#</a></h3>
<p>The endpoint uses URL-encoded variable substitution:</p>
<ul>
<li><code>%24</code> = <code>$</code> (dollar sign)</li>
<li><code>%0A</code> = <code>\n</code> (newline)</li>
<li><strong>Any variable name works</strong>: <code>a</code>, <code>b</code>, <code>c</code>, <code>d</code>, <code>cmd</code>, <code>command</code>, etc.</li>
</ul>
<h3 id="proof-of-concept">Proof of Concept<a class="heading-anchor" href="#proof-of-concept" aria-label="Copy link to “Proof of Concept”">#</a></h3>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">curl -k -u admin:password \
  <span class="hljs-string">"https://192.168.103.143/%24b%0A?b=show%20version%20|%20include%20uptime"</span>

<span class="hljs-comment"># Returns:</span>
stratix <span class="hljs-built_in">uptime</span> is 11 hours, 42 minutes
</code></pre></div>
<p><img src="https://media.ics.red/images/stratix/examplerce.png" alt="Successful RCE Execution" decoding="async" loading="lazy" width="836" height="425"></p>
<p><em>Terminal showing successful command execution via the GET-based RCE endpoint</em></p>
<h3 id="confirmed-working-commands">Confirmed Working Commands<a class="heading-anchor" href="#confirmed-working-commands" aria-label="Copy link to “Confirmed Working Commands”">#</a></h3>








































<div class="table-wrap" tabindex="0" role="region" aria-label="Table"><table><thead><tr><th>Command</th><th>Output Size</th><th>Sensitive Data</th></tr></thead><tbody><tr><td><code>show running-config</code></td><td>4,700 bytes</td><td>Passwords, SNMP strings, network config</td></tr><tr><td><code>show startup-config</code></td><td>3,354 bytes</td><td>Saved configuration</td></tr><tr><td><code>show version</code></td><td>2,921 bytes</td><td>IOS version, serial numbers</td></tr><tr><td><code>show users</code></td><td>142 bytes</td><td>Active sessions</td></tr><tr><td><code>show ip interface brief</code></td><td>820 bytes</td><td>Network topology</td></tr><tr><td><code>dir flash:</code></td><td>1,113 bytes</td><td>File system contents</td></tr></tbody></table></div>
<hr>
<h2 id="finding-2-csrf-leading-to-unauthenticated-rce">Finding #2: CSRF Leading to Unauthenticated RCE<a class="heading-anchor" href="#finding-2-csrf-leading-to-unauthenticated-rce" aria-label="Copy link to “Finding #2: CSRF Leading to Unauthenticated RCE”">#</a></h2>
<p>Because the RCE endpoint uses <strong>GET requests with no CSRF token</strong>, any webpage can trigger command execution when visited by an authenticated administrator.</p>
<h3 id="attack-scenario">Attack Scenario<a class="heading-anchor" href="#attack-scenario" aria-label="Copy link to “Attack Scenario”">#</a></h3>
<ol>
<li>Attacker creates malicious webpage</li>
<li>Sends link to network administrator</li>
<li>Admin clicks link while logged into switch web interface</li>
<li>Malicious JavaScript executes commands in admin's browser context</li>
<li>Full device configuration exfiltrated to attacker's server</li>
</ol>
<hr>
<h2 id="finding-3-password-hash-exposure">Finding #3: Password Hash Exposure<a class="heading-anchor" href="#finding-3-password-hash-exposure" aria-label="Copy link to “Finding #3: Password Hash Exposure”">#</a></h2>
<p>The <code>useraccounts.shtml</code> page includes a hidden textarea that contains the output of SSI commands, exposing all user account password hashes in the HTML source code.</p>
<div class="code-block" data-lang="html"><div class="code-block__bar"><span class="code-block__lang">html</span></div><pre><code class="hljs language-html"><span class="hljs-tag">&#x3C;<span class="hljs-name">textarea</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"display:none; visibility:hidden;"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"USER_SUMMARY"</span>></span>
    <span class="hljs-comment">&#x3C;!--#exec cmd='more system:running-config | i user'--></span>
<span class="hljs-tag">&#x3C;/<span class="hljs-name">textarea</span>></span>
</code></pre></div>
<p>When rendered:</p>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code>username admin privilege 15 secret 5 $1$P4Bi$8t74cAAknpCiVCvL7IC6T.
username vulntest privilege 5 secret 9 $9$wLzi8Cmi6aH91k$qOAH1kOzmLhTX1tKqGx/...
</code></pre></div>
<p><img src="https://media.ics.red/images/stratix/hashleak.png" alt="Password Hash Exposure" decoding="async" loading="lazy" width="1552" height="206"></p>
<p><em>Screenshot showing admin password hash visible in page source to read-only user</em></p>
<hr>
<h2 id="the-complete-attack-chain">The Complete Attack Chain<a class="heading-anchor" href="#the-complete-attack-chain" aria-label="Copy link to “The Complete Attack Chain”">#</a></h2>
<h3 id="phase-1-initial-access">Phase 1: Initial Access<a class="heading-anchor" href="#phase-1-initial-access" aria-label="Copy link to “Phase 1: Initial Access”">#</a></h3>
<p>Attacker obtains read-only credentials (default/weak passwords)</p>
<h3 id="phase-2-information-disclosure">Phase 2: Information Disclosure<a class="heading-anchor" href="#phase-2-information-disclosure" aria-label="Copy link to “Phase 2: Information Disclosure”">#</a></h3>
<p>Access useraccounts.shtml - extract admin password hash from HTML source</p>
<h3 id="phase-3-offline-password-cracking">Phase 3: Offline Password Cracking<a class="heading-anchor" href="#phase-3-offline-password-cracking" aria-label="Copy link to “Phase 3: Offline Password Cracking”">#</a></h3>
<p>Run hashcat on MD5-crypt hash - cracked in ~15 minutes with GPU</p>
<h3 id="phase-4-admin-access">Phase 4: Admin Access<a class="heading-anchor" href="#phase-4-admin-access" aria-label="Copy link to “Phase 4: Admin Access”">#</a></h3>
<p>Login with cracked admin credentials - full web interface access</p>
<h3 id="phase-5-remote-code-execution">Phase 5: Remote Code Execution<a class="heading-anchor" href="#phase-5-remote-code-execution" aria-label="Copy link to “Phase 5: Remote Code Execution”">#</a></h3>
<p>Use GET-based RCE endpoint - extract all configuration, passwords, network topology</p>
<hr>
<p><strong>Disclaimer:</strong> This assessment was performed on authorized equipment for security research purposes. All findings are reported in good faith to improve security.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[DNP3 Timing Attack: Weaponizing Time Synchronization in Industrial Control Systems]]></title>
            <link>https://ics.red/posts/dnp3-timing-attack</link>
            <guid isPermaLink="false">https://ics.red/posts/dnp3-timing-attack</guid>
            <pubDate>Thu, 14 Aug 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[A deep dive into exploiting DNP3's time synchronization mechanism to disrupt critical infrastructure operations]]></description>
            <content:encoded><![CDATA[<h2 id="introduction">Introduction<a class="heading-anchor" href="#introduction" aria-label="Copy link to “Introduction”">#</a></h2>
<p>Industrial Control Systems (ICS) rely heavily on accurate time synchronization for coordinating operations, maintaining event logs, and executing time-dependent control logic. The Distributed Network Protocol (DNP3), widely deployed in electric utilities and critical infrastructure, includes a time synchronization feature that can be exploited by adversaries to cause significant disruption.</p>
<p>I've developed a proof-of-concept tool that demonstrates how DNP3's LAN time-sync procedure can be weaponized to send arbitrary timestamps to outstations, potentially compromising the integrity of time-dependent operations across critical infrastructure.</p>
<h2 id="what-is-dnp3">What is DNP3?<a class="heading-anchor" href="#what-is-dnp3" aria-label="Copy link to “What is DNP3?”">#</a></h2>
<p>DNP3 (Distributed Network Protocol 3) is commonly used in SCADA systems for communication between control centers and RTUs or IEDs. It's particularly prevalent in:</p>
<ul>
<li>Electric power transmission and distribution</li>
<li>Water and wastewater systems</li>
<li>Oil and gas pipelines</li>
<li>Transportation systems</li>
</ul>
<p>The protocol operates on standard ports (typically TCP 20000 and 19999).</p>
<h2 id="the-time-synchronization-vulnerability">The Time Synchronization Vulnerability<a class="heading-anchor" href="#the-time-synchronization-vulnerability" aria-label="Copy link to “The Time Synchronization Vulnerability”">#</a></h2>
<p>DNP3 includes a time synchronization mechanism (function code 0x17) with several concerning characteristics:</p>
<ol>
<li><strong>Lack of Authentication</strong>: Time-sync messages typically lack cryptographic authentication</li>
<li><strong>Unrestricted Values</strong>: The protocol accepts arbitrary timestamp values without validation</li>
<li><strong>Wide Impact</strong>: A single malicious time-sync message can affect multiple dependent systems</li>
<li><strong>Silent Execution</strong>: Time modifications may not trigger obvious alarms</li>
</ol>
<h2 id="attack-scenarios-and-impact">Attack Scenarios and Impact<a class="heading-anchor" href="#attack-scenarios-and-impact" aria-label="Copy link to “Attack Scenarios and Impact”">#</a></h2>
<h3 id="1-operational-sequencing-disruption">1. Operational Sequencing Disruption<a class="heading-anchor" href="#1-operational-sequencing-disruption" aria-label="Copy link to “1. Operational Sequencing Disruption”">#</a></h3>
<ul>
<li>Circuit breaker operations may execute out of sequence</li>
<li>Load shedding algorithms may make incorrect decisions</li>
<li>Automated protective relays may fail to coordinate</li>
</ul>
<h3 id="2-log-file-corruption-and-forensic-evasion">2. Log File Corruption and Forensic Evasion<a class="heading-anchor" href="#2-log-file-corruption-and-forensic-evasion" aria-label="Copy link to “2. Log File Corruption and Forensic Evasion”">#</a></h3>
<ul>
<li>Historical data appears in wrong temporal order</li>
<li>Forensic investigations become significantly more difficult</li>
<li>Sequence of Events (SOE) logs become unreliable</li>
</ul>
<h3 id="3-time-dependent-control-logic-manipulation">3. Time-Dependent Control Logic Manipulation<a class="heading-anchor" href="#3-time-dependent-control-logic-manipulation" aria-label="Copy link to “3. Time-Dependent Control Logic Manipulation”">#</a></h3>
<ul>
<li>Scheduled maintenance windows triggered prematurely</li>
<li>Time-of-use pricing mechanisms circumvented</li>
<li>Automated switching based on time schedules may malfunction</li>
</ul>
<h2 id="the-tool-dnp3-timing-attack">The Tool: DNP3 Timing Attack<a class="heading-anchor" href="#the-tool-dnp3-timing-attack" aria-label="Copy link to “The Tool: DNP3 Timing Attack”">#</a></h2>
<p>I developed a Rust-based command-line tool to demonstrate this attack vector.</p>
<h3 id="features">Features<a class="heading-anchor" href="#features" aria-label="Copy link to “Features”">#</a></h3>
<ul>
<li><strong>Arbitrary Timestamp Injection</strong>: Send any timestamp value to target outstations</li>
<li><strong>Multiple Target Support</strong>: Attack multiple RTUs simultaneously</li>
<li><strong>Port Flexibility</strong>: Support for standard and non-standard DNP3 ports</li>
<li><strong>Timing Control</strong>: Configurable delays and timing patterns</li>
<li><strong>Rust Safety</strong>: Memory-safe implementation with strong type guarantees</li>
</ul>
<h2 id="defensive-considerations">Defensive Considerations<a class="heading-anchor" href="#defensive-considerations" aria-label="Copy link to “Defensive Considerations”">#</a></h2>
<h3 id="network-segmentation">Network Segmentation<a class="heading-anchor" href="#network-segmentation" aria-label="Copy link to “Network Segmentation”">#</a></h3>
<ul>
<li>Isolate DNP3 traffic to dedicated OT networks</li>
<li>Implement strict firewall rules</li>
<li>Deploy unidirectional gateways where appropriate</li>
</ul>
<h3 id="authentication-and-encryption">Authentication and Encryption<a class="heading-anchor" href="#authentication-and-encryption" aria-label="Copy link to “Authentication and Encryption”">#</a></h3>
<ul>
<li>Upgrade to DNP3 Secure Authentication (SAv5/SAv6)</li>
<li>Implement encrypted tunnels for DNP3 communications</li>
</ul>
<h3 id="monitoring-and-detection">Monitoring and Detection<a class="heading-anchor" href="#monitoring-and-detection" aria-label="Copy link to “Monitoring and Detection”">#</a></h3>
<ul>
<li>Monitor for unexpected time-sync messages</li>
<li>Implement anomaly detection for timestamp deviations</li>
<li>Alert on time jumps exceeding defined thresholds</li>
</ul>
<h2 id="responsible-disclosure">Responsible Disclosure<a class="heading-anchor" href="#responsible-disclosure" aria-label="Copy link to “Responsible Disclosure”">#</a></h2>
<p>This research is released for educational and authorized security testing purposes only.</p>
<h2 id="project-links">Project Links<a class="heading-anchor" href="#project-links" aria-label="Copy link to “Project Links”">#</a></h2>
<ul>
<li><strong>GitHub Repository</strong>: <a href="https://github.com/ladderlogix/DNP3TimmingAttack">DNP3TimmingAttack</a></li>
<li><strong>License</strong>: AGPL-3.0</li>
<li><strong>Language</strong>: Rust</li>
</ul>
<hr>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Dangers of IoT Devices Presentation]]></title>
            <link>https://ics.red/posts/iot-presentation</link>
            <guid isPermaLink="false">https://ics.red/posts/iot-presentation</guid>
            <pubDate>Thu, 24 Apr 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Presentation about the dangers of IoT devices given at CvCISO coffee talks]]></description>
            <content:encoded><![CDATA[<p>Slides from a talk I gave at a CvCISO coffee talk on the security risks of everyday IoT devices.</p>
<p><a href="https://bucket.ics.red/IoTPres.pptx">Download the slides (PowerPoint, 19.5 MB)</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Homelab]]></title>
            <link>https://ics.red/posts/homelab</link>
            <guid isPermaLink="false">https://ics.red/posts/homelab</guid>
            <pubDate>Wed, 01 Jan 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Pictures and description of my homelab]]></description>
            <content:encoded><![CDATA[<h2 id="my-homelab-a-cybersecurity-professionals-hardware-paradise">My Homelab: A Cybersecurity Professional's Hardware Paradise<a class="heading-anchor" href="#my-homelab-a-cybersecurity-professionals-hardware-paradise" aria-label="Copy link to “My Homelab: A Cybersecurity Professional&#x27;s Hardware Paradise”">#</a></h2>
<p>Welcome to my homelab - a carefully crafted environment that serves as both my learning playground and professional development space.</p>
<h2 id="the-heart-of-operations-central-server-rack">The Heart of Operations: Central Server Rack<a class="heading-anchor" href="#the-heart-of-operations-central-server-rack" aria-label="Copy link to “The Heart of Operations: Central Server Rack”">#</a></h2>
<h3 id="core-infrastructure">Core Infrastructure<a class="heading-anchor" href="#core-infrastructure" aria-label="Copy link to “Core Infrastructure”">#</a></h3>
<ul>
<li><strong>HPE ProLiant DL350 Gen10</strong>: Primary ESXi hypervisor server</li>
<li><strong>4TB SSD NAS</strong>: Connected via 10Gb link for high-speed external storage</li>
<li><strong>Cisco 2960L Switch</strong>: Main distribution switch</li>
<li><strong>pfSense Firewall</strong>: Primary security gateway</li>
</ul>
<h2 id="industrial-control-systems-ics-laboratory">Industrial Control Systems (ICS) Laboratory<a class="heading-anchor" href="#industrial-control-systems-ics-laboratory" aria-label="Copy link to “Industrial Control Systems (ICS) Laboratory”">#</a></h2>
<h3 id="current-ics-equipment">Current ICS Equipment<a class="heading-anchor" href="#current-ics-equipment" aria-label="Copy link to “Current ICS Equipment”">#</a></h3>
<ul>
<li><strong>Arduino OPTA</strong>: Modern industrial-grade PLC</li>
<li><strong>Allen-Bradley MicroLogix 1100</strong>: Classic PLC</li>
<li><strong>Cisco ISA Firewall</strong>: Specialized security appliance</li>
<li><strong>Honeywell Cipher 50</strong>: Industrial communication device</li>
<li><strong>Schneider Electric PLC</strong>: Additional PLC for diverse testing</li>
</ul>
<h2 id="network-security-and-monitoring">Network Security and Monitoring<a class="heading-anchor" href="#network-security-and-monitoring" aria-label="Copy link to “Network Security and Monitoring”">#</a></h2>
<p><strong>Teleseer</strong> running on VMware, connected to a span port on the Cisco switch.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[WWHF Badge Hacking: 2024 Deadwood & 2025 Mile High]]></title>
            <link>https://ics.red/posts/wwhf-2024-badge</link>
            <guid isPermaLink="false">https://ics.red/posts/wwhf-2024-badge</guid>
            <pubDate>Sun, 20 Oct 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Dumping and analyzing the ESP32 firmware on the Wild West Hacking Fest 2024 Deadwood and 2025 Mile High badges: NVS secrets, MQTT, flag solutions, and a few hardware fixes.]]></description>
            <content:encoded><![CDATA[<p>I want to thank everyone who put on the conference and made the badge. I had a blast and am very thankful for my team.</p>
<p>This is a mashup of both 2024 and 2025 badge hacking. While the ideas and process are the same, some of the output information might be different, and there are separate sections for each year.</p>
<h2 id="about-us">About Us<a class="heading-anchor" href="#about-us" aria-label="Copy link to “About Us”">#</a></h2>
<h3 id="team">Team<a class="heading-anchor" href="#team" aria-label="Copy link to “Team”">#</a></h3>
<ul>
<li>Stephen Glombicki / Logix (@ladderlogic)</li>
<li>Cooper Wiegand / shiloh (@cw0)</li>
</ul>
<p><strong>2024 Team Only</strong></p>
<ul>
<li>Victor Yakovlyev / chickn nuggies (@chickinnuggies)</li>
<li>Jon Sullivan / vort (@vort4282)</li>
<li>Rob Sullivan (@rsulliva)</li>
</ul>
<h3 id="other-authors">Other Authors<a class="heading-anchor" href="#other-authors" aria-label="Copy link to “Other Authors”">#</a></h3>
<p>Parts on how to solve some of the flags from 2024 are taken from cw0. His writeup can be found <a href="https://github.com/Cooperw/ctf/tree/master/2024-10-11-wwhf24">here</a>. I tried to make mine more beginner-friendly and show the setup process.</p>
<h2 id="setup">Setup<a class="heading-anchor" href="#setup" aria-label="Copy link to “Setup”">#</a></h2>
<p>To start attacking the ESP32 chip we should see what is in the firmware. To do this we will use esptool to dump.</p>
<h3 id="install-esptool">Install Esptool<a class="heading-anchor" href="#install-esptool" aria-label="Copy link to “Install Esptool”">#</a></h3>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">pip install esptool
</code></pre></div>
<h3 id="identify-esp32-com-port">Identify ESP32 COM Port<a class="heading-anchor" href="#identify-esp32-com-port" aria-label="Copy link to “Identify ESP32 COM Port”">#</a></h3>
<p>Finding the COM port can be useful as not all tools can automatically find what you are looking for. The steps depend on your platform; pick your OS or the Arduino IDE below.</p>
<h4 id="windows">Windows<a class="heading-anchor" href="#windows" aria-label="Copy link to “Windows”">#</a></h4>
<ol>
<li>
<p>Press Windows Key + X</p>
</li>
<li>
<p>Press <strong>Device Manager</strong> to launch Device Manager.</p>
<p><img src="/writeups/wwhf-2024-badge/windows-device-manager-launch.webp" alt="Windows Win+X menu with Device Manager highlighted" decoding="async" loading="lazy" width="231" height="652"></p>
</li>
<li>
<p>Scroll down to <strong>Ports (COM &#x26; LPT)</strong> and expand the menu</p>
</li>
<li>
<p>Plug your badge into your computer and turn the badge on.</p>
</li>
<li>
<p>The device tree will refresh and you should see a new device under <strong>Ports (COM &#x26; LPT)</strong>. This will be the communication port used to talk to the device.</p>
<p><img src="/writeups/wwhf-2024-badge/windows-device-manager-com-port.webp" alt="Device Manager showing the new USB Serial Device (COM16) under Ports (COM &#x26; LPT)" decoding="async" loading="lazy" width="800" height="598"></p>
</li>
</ol>
<h4 id="linux">Linux<a class="heading-anchor" href="#linux" aria-label="Copy link to “Linux”">#</a></h4>
<p>This command will list out all active serial devices</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash"><span class="hljs-built_in">ls</span> /dev/tty*
</code></pre></div>
<p>You may need to try a couple to see which one works or you can run this following command to see what each one is</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash"><span class="hljs-keyword">for</span> dev <span class="hljs-keyword">in</span> /dev/ttyUSB*; <span class="hljs-keyword">do</span> <span class="hljs-built_in">sudo</span> udevadm info -q all -n <span class="hljs-string">"<span class="hljs-variable">$dev</span>"</span> | grep -E <span class="hljs-string">'DEVNAME|ID_MODEL'</span>; <span class="hljs-keyword">done</span>
</code></pre></div>
<h4 id="mac">Mac<a class="heading-anchor" href="#mac" aria-label="Copy link to “Mac”">#</a></h4>
<p>This command will list out all active serial devices</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash"><span class="hljs-built_in">ls</span> /dev/cu.*
</code></pre></div>
<p>You may need to try a couple to see which one works or you can run this following command to see what each one is</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash"><span class="hljs-built_in">ls</span> /dev/cu.* | grep -i usb | <span class="hljs-keyword">while</span> <span class="hljs-built_in">read</span> dev; <span class="hljs-keyword">do</span> ioreg -p IOUSB -l -w 0 | grep -A 10 <span class="hljs-string">"<span class="hljs-variable">$dev</span>"</span>; <span class="hljs-keyword">done</span>
</code></pre></div>
<h4 id="arduino-ide">Arduino IDE<a class="heading-anchor" href="#arduino-ide" aria-label="Copy link to “Arduino IDE”">#</a></h4>
<ol>
<li>Install the Arduino IDE</li>
<li><img src="/writeups/wwhf-2024-badge/arduino-ide-port-menu.webp" alt="Arduino IDE Tools > Port menu listing the available serial ports" decoding="async" loading="lazy" width="497" height="506"></li>
</ol>
<h3 id="dumping-flash">Dumping Flash<a class="heading-anchor" href="#dumping-flash" aria-label="Copy link to “Dumping Flash”">#</a></h3>
<h4 id="understanding-the-chip">Understanding the Chip<a class="heading-anchor" href="#understanding-the-chip" aria-label="Copy link to “Understanding the Chip”">#</a></h4>
<p>To dump the flash we will need to know two things: the port and the size of the flash. To find the size of the flash we can run the following command</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">python -m esptool --port {COMPORT} flash_id
</code></pre></div>
<p>Sample Output:</p>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">C:\Users\NSSte>python -m esptool --port COM16 flash_id
esptool.py v4.8.1
Serial port COM16
Connecting...
Detecting chip type... ESP32-S3
Chip is ESP32-S3 (QFN56) (revision v0.2)
Features: WiFi, BLE
Crystal is 40MHz
MAC: 24:58:7c:f4:8e:30
Uploading stub...
Running stub...
Stub running...
Manufacturer: c8
Device: 4017
Detected flash size: 8MB
Flash type set in eFuse: quad (4 data lines)
Flash voltage set by a strapping pin to 3.3V
Hard resetting via RTS pin...
</code></pre></div>
<p>We see we have 8MB of flash, and we also have the MAC address in case we lose the display</p>
<p>More commands can be found in the <a href="https://docs.espressif.com/projects/esptool/en/latest/esp32s3/esptool/basic-commands.html#read-flash-contents-read-flash">docs</a></p>
<h3 id="reading-the-flash-with-esptool">Reading the Flash with esptool<a class="heading-anchor" href="#reading-the-flash-with-esptool" aria-label="Copy link to “Reading the Flash with esptool”">#</a></h3>
<p>Using another command from the <a href="https://docs.espressif.com/projects/esptool/en/latest/esp32s3/esptool/basic-commands.html#read-flash-contents-read-flash">docs</a> called read flash we will now dump out the flash contents</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">python -m esptool -p {COMPORT} -b 460800 read_flash 0 ALL flash_contents.bin
</code></pre></div>
<p>We now should have an 8MB flash file.</p>
<h2 id="analyzing-flash-dump">Analyzing Flash Dump<a class="heading-anchor" href="#analyzing-flash-dump" aria-label="Copy link to “Analyzing Flash Dump”">#</a></h2>
<div class="callout callout--note" role="note"><p class="callout__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="callout__icon"><path d="M12 8h.01"></path><path d="M11 12h1v4h1"></path><path d="M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18z"></path></svg>Note</p>
<p>This was written with the 2024 badge. While the steps remain the same, the data/passwords/URLs have changed.</p>
</div>
<p>I will be using <a href="https://github.com/tenable/esp32_image_parser">esp32 image parser</a> to analyze, instead of <a href="https://github.com/BlackVS/esp32knife">esp32knife</a>, because I could not get esp32knife to recognize my dump or get it to dump my firmware.</p>
<h3 id="installing-esp32-image-parser">Installing esp32 image parser<a class="heading-anchor" href="#installing-esp32-image-parser" aria-label="Copy link to “Installing esp32 image parser”">#</a></h3>
<h4 id="download-the-repo">Download the repo<a class="heading-anchor" href="#download-the-repo" aria-label="Copy link to “Download the repo”">#</a></h4>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">git <span class="hljs-built_in">clone</span> https://github.com/tenable/esp32_image_parser
</code></pre></div>
<h4 id="install-requirements">Install Requirements<a class="heading-anchor" href="#install-requirements" aria-label="Copy link to “Install Requirements”">#</a></h4>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">python -m pip install -r requirements.txt
</code></pre></div>
<h3 id="showing-the-dump-partitions">Showing the dump partitions<a class="heading-anchor" href="#showing-the-dump-partitions" aria-label="Copy link to “Showing the dump partitions”">#</a></h3>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">python esp32_image_parser.py show_partitions badge_flash.bin
</code></pre></div>
<h4 id="annotated-output">Annotated Output<a class="heading-anchor" href="#annotated-output" aria-label="Copy link to “Annotated Output”">#</a></h4>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">└─$ python esp32_image_parser.py show_partitions badge_flash.bin
reading partition table...
entry 0:
  label      : nvs       -> Non-volatile Storage
  offset     : 0x9000
  length     : 20480
  type       : 1 [DATA]
  sub type   : 2 [WIFI]

entry 1:
  label      : otadata
  offset     : 0xe000
  length     : 8192
  type       : 1 [DATA]
  sub type   : 0 [OTA]

entry 2:
  label      : app0       -> Core 0 Program
  offset     : 0x10000
  length     : 3342336
  type       : 0 [APP]
  sub type   : 16 [ota_0]

entry 3:
  label      : app1       -> Core 1 Program
  offset     : 0x340000
  length     : 3342336
  type       : 0 [APP]
  sub type   : 17 [ota_1]

entry 4:
  label      : spiffs     -> File Systems
  offset     : 0x670000
  length     : 1572864
  type       : 1 [DATA]
  sub type   : 130 [unknown]

entry 5:
  label      : coredump   -> Crash Dumps
  offset     : 0x7f0000
  length     : 65536
  type       : 1 [DATA]
  sub type   : 3 [unknown]

MD5sum:
467eb896e29d1aa938c557f4cdfc4c5b
Done
</code></pre></div>
<h3 id="pulling-data-out-of-the-partitions">Pulling Data Out of the Partitions<a class="heading-anchor" href="#pulling-data-out-of-the-partitions" aria-label="Copy link to “Pulling Data Out of the Partitions”">#</a></h3>
<h4 id="reading-non-volatile-storage">Reading Non-Volatile Storage<a class="heading-anchor" href="#reading-non-volatile-storage" aria-label="Copy link to “Reading Non-Volatile Storage”">#</a></h4>
<p>ESP32 Image Parser can dump NVS data</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">python esp32_image_parser.py show_partitions badge_flash.bin
</code></pre></div>
<h5 id="truncated-output">Truncated Output</h5>
<div class="code-block code-block--collapsible"><input type="checkbox" id="code-more-1" class="code-block__toggle"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">└─$ python esp32_image_parser.py dump_nvs badge.bin -partition nvs
Dumping partition 'nvs' to nvs_out.bin
Page 0
  page state : FULL
  page seq no. : 0
  page version : 2
  crc 32 : 3115986308
  page entry state bitmap (decoded) : 222222222222222222222222202222222222222222222222222222222222222222222222222222222222222222222222222222222222202222222222222233
  Entry 0
  Bitmap State : Written
    Written Entry 0
      NS Index : 0
      Type : U8
      Span : 1
      ChunkIndex : 255
      Key : badge_config
      Data (U8) : 1

  Entry 1
  Bitmap State : Written
    Written Entry 1
      NS Index : 1
          NS : badge_config
      Type : STR
      Span : 3
      ChunkIndex : 255
      Key : ServerPath
      String :
        Size : 38
        Data : https://wwhf2024.s3.amazonaws.com/v2/

  Entry 4
  Bitmap State : Written
    Written Entry 4
      NS Index : 1
          NS : badge_config
      Type : STR
      Span : 2
      ChunkIndex : 255
      Key : FirmwareName
      String :
        Size : 13
        Data : wwhf2024.bin

  Entry 6
  Bitmap State : Written
    Written Entry 6
      NS Index : 1
          NS : badge_config
      Type : I32
      Span : 1
      ChunkIndex : 255
      Key : HardwareType
      Data (I32) : 4

  Entry 7
  Bitmap State : Written
    Written Entry 7
      NS Index : 1
          NS : badge_config
      Type : STR
      Span : 2
      ChunkIndex : 255
      Key : SSIDName
      String :
        Size : 12
        Data : WWHF Badges

  Entry 9
  Bitmap State : Written
    Written Entry 9
      NS Index : 1
          NS : badge_config
      Type : STR
      Span : 2
      ChunkIndex : 255
      Key : SSIDPassword
      String :
        Size : 18
        Data : 77HackTheEsp3too@

  Entry 11
  Bitmap State : Written
    Written Entry 11
      NS Index : 1
          NS : badge_config
      Type : STR
      Span : 3
      ChunkIndex : 255
      Key : MQTTBroker
      String :
        Size : 34
        Data : w6e1deed.ala.us-east-1.emqxsl.com

  Entry 14
  Bitmap State : Written
    Written Entry 14
      NS Index : 1
          NS : badge_config
      Type : STR
      Span : 2
      ChunkIndex : 255
      Key : MQTTBroadcast
      String :
        Size : 17
        Data : broadcast/action

  Entry 16
  Bitmap State : Written
    Written Entry 16
      NS Index : 1
          NS : badge_config
      Type : STR
      Span : 2
      ChunkIndex : 255
      Key : MQTTEvents
      String :
        Size : 14
        Data : events/device

  Entry 18
  Bitmap State : Written
    Written Entry 18
      NS Index : 1
          NS : badge_config
      Type : STR
      Span : 2
      ChunkIndex : 255
      Key : MQTTUsername
      String :
        Size : 7
        Data : badges

  Entry 20
  Bitmap State : Written
    Written Entry 20
      NS Index : 1
          NS : badge_config
      Type : STR
      Span : 2
      ChunkIndex : 255
      Key : MQTTPassword
      String :
        Size : 20
        Data : TPY_net1pvg*ywf.cjk

  Entry 22
  Bitmap State : Written
    Written Entry 22
      NS Index : 1
          NS : badge_config
      Type : I32
      Span : 1
      ChunkIndex : 255
      Key : MQTTPort
      Data (I32) : 8883

  Entry 23
  Bitmap State : Written
    Written Entry 23
      NS Index : 1
          NS : badge_config
      Type : STR
      Span : 2
      ChunkIndex : 255
      Key : DeviceName
      String :
        Size : 14
        Data : HaySupernova2
</code></pre><label for="code-more-1" class="code-block__more"><span class="code-block__more-closed">Show all 169 lines</span><span class="code-block__more-open">Show less</span></label></div>
<h4 id="extracting-app0-app1-and-other-partitions">Extracting App0, App1 and Other Partitions<a class="heading-anchor" href="#extracting-app0-app1-and-other-partitions" aria-label="Copy link to “Extracting App0, App1 and Other Partitions”">#</a></h4>
<p>While not the most needed, it is possible to pull out app0 using this program. This could be used to convert to an ELF, but at this time no program that I know of can convert this without errors.</p>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">└─$ python esp32_image_parser.py dump_partition badge_flash.bin -partition app0
Dumping partition 'app0' to app0_out.bin
</code></pre></div>
<h3 id="reviewing-creds-found-in-nvs">Reviewing Creds found in NVS<a class="heading-anchor" href="#reviewing-creds-found-in-nvs" aria-label="Copy link to “Reviewing Creds found in NVS”">#</a></h3>
<p>We see three big systems of interest in NVS</p>
<h3 id="wifi-creds">Wifi Creds<a class="heading-anchor" href="#wifi-creds" aria-label="Copy link to “Wifi Creds”">#</a></h3>
<p>We can pull the SSID and Password</p>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">SSID: WWHF Badges
Password: 77HackTheEsp3too@
</code></pre></div>
<p>There is nothing of interest using that as all transmissions are encrypted, but if you are not connected to the official Wi-Fi you can create your own.</p>
<h3 id="aws-bucket">AWS Bucket<a class="heading-anchor" href="#aws-bucket" aria-label="Copy link to “AWS Bucket”">#</a></h3>
<p>We see where the bin files are being stored</p>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">https://wwhf2024.s3.amazonaws.com/v2/wwhf2024.bin
</code></pre></div>
<p>and we can guess and see that there is another one at</p>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">https://wwhf2024.s3.amazonaws.com/v1/wwhf2024.bin
</code></pre></div>
<h3 id="mqtt">MQTT<a class="heading-anchor" href="#mqtt" aria-label="Copy link to “MQTT”">#</a></h3>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">URL:  w6e1deed.ala.us-east-1.emqxsl.com
Username: badges
Password: TPY_net1pvg*ywf.cjk
MQTT Port: 8883

Subscriptions
	- broadcast/action
	- events/device
	- badges (Do not worry see extra for more info on this one)
</code></pre></div>
<h3 id="connecting-to-mqtt">Connecting to MQTT<a class="heading-anchor" href="#connecting-to-mqtt" aria-label="Copy link to “Connecting to MQTT”">#</a></h3>
<p>We can either use an online tool or download a MQTT client application</p>
<h4 id="online-tool">Online Tool<a class="heading-anchor" href="#online-tool" aria-label="Copy link to “Online Tool”">#</a></h4>
<p>I found this <a href="https://mqttx.app/web-client">site</a> to work the best</p>
<p><img src="/writeups/wwhf-2024-badge/mqttx-web-connection-settings.webp" alt="MQTTX web client connection settings using the WebSocket port 8084" decoding="async" loading="lazy" width="1600" height="407"></p>
<p>This tool only works with WebSockets, so set it to the WebSocket port of <code>8084</code>. Use the rest of the credentials found in NVS.</p>
<p><img src="/writeups/wwhf-2024-badge/mqttx-web-messages.webp" alt="MQTTX web client subscribed to +/# and receiving badge messages" decoding="async" loading="lazy" width="1600" height="863"></p>
<p>Subscribe to the topics listed, or use <code>+/#</code> to subscribe to all. We have to use this as <code>#</code> is not allowed.</p>
<h4 id="mqtt-client-app">MQTT Client App<a class="heading-anchor" href="#mqtt-client-app" aria-label="Copy link to “MQTT Client App”">#</a></h4>
<p>I will use MQTT Explorer as the desktop client to see MQTT messages.</p>
<p><img src="/writeups/wwhf-2024-badge/mqtt-explorer-connection.webp" alt="MQTT Explorer connection settings with encryption (TLS) on and certificate validation off" decoding="async" loading="lazy" width="874" height="464"></p>
<p>Use the settings found in NVS. Set validate certificate to false and encryption (TLS) to true.</p>
<p><img src="/writeups/wwhf-2024-badge/mqtt-explorer-subscriptions.webp" alt="MQTT Explorer advanced settings adding a +/# topic subscription" decoding="async" loading="lazy" width="859" height="451"></p>
<p>Add a subscription to the topics you want. I recommend <code>+/#</code> as it will let you see every message.</p>
<h2 id="2024-deadwood-flags">2024 Deadwood Flags<a class="heading-anchor" href="#2024-deadwood-flags" aria-label="Copy link to “2024 Deadwood Flags”">#</a></h2>
<h3 id="deadwood-flag-1">Deadwood Flag 1<a class="heading-anchor" href="#deadwood-flag-1" aria-label="Copy link to “Deadwood Flag 1”">#</a></h3>
<p>There are two ways to solve this: either by finding the string in the dump or by physically analyzing the badge.</p>
<h4 id="physical">Physical<a class="heading-anchor" href="#physical" aria-label="Copy link to “Physical”">#</a></h4>
<ol>
<li>Begin by powering your badge and then recording the LED sequences flashing on the UFO. (I have a recording <a href="https://youtube.com/shorts/edSQGYkBatY?feature=share">here</a>)</li>
<li>Transcribe the 8 LEDs to binary strings</li>
</ol>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">01010011
01001001
01000111
01111011
01100011
00110100
01101110
01110100
01011111
01110011
01110100
00110000
01110000
01011111
01110100
01101000
00110011
01011111
01110011
00110001
01100111
01101110
00110100
01101100
01111101
</code></pre></div>
<ol start="3">
<li>Construct a <a href="https://gchq.github.io/CyberChef/#recipe=From_Binary(&#x27;Space&#x27;,8)&#x26;input=MDEwMTAwMTEKMDEwMDEwMDEKMDEwMDAxMTEKMDExMTEwMTEKMDExMDAwMTEKMDAxMTAxMDAKMDExMDExMTAKMDExMTAxMDAKMDEwMTExMTEKMDExMTAwMTEKMDExMTAxMDAKMDAxMTAwMDAKMDExMTAwMDAKMDEwMTExMTEKMDExMTAxMDAKMDExMDEwMDAKMDAxMTAwMTEKMDEwMTExMTEKMDExMTAwMTEKMDAxMTAwMDEKMDExMDAxMTEKMDExMDExMTAKMDAxMTAxMDAKMDExMDExMDAKMDExMTExMDE">CyberChef recipe: FromBinary_8</a></li>
</ol>
<h4 id="firmware-strings">Firmware Strings<a class="heading-anchor" href="#firmware-strings" aria-label="Copy link to “Firmware Strings”">#</a></h4>
<p>I threw the bin file found in the S3 bucket into a disassembler.</p>
<h5 id="ida-pro">IDA Pro</h5>
<p>This is probably the best tool to analyze the binary but with the high cost it may be unavailable to most. I searched for <code>{</code> in strings to hunt down the flag.</p>
<p><img src="/writeups/wwhf-2024-badge/ida-strings-flag-1.webp" alt="IDA Pro strings view filtered on SIG{ showing the Flag 1 string" decoding="async" loading="lazy" width="894" height="995"></p>
<h5 id="ghidra">Ghidra</h5>
<p><img src="/writeups/wwhf-2024-badge/ghidra-language-xtensa.webp" alt="Ghidra language selection with Xtensa 32-bit little-endian selected" decoding="async" loading="lazy" width="501" height="369"></p>
<p>You will want to set your language to <strong>Xtensa Little Endian</strong></p>
<p><img src="/writeups/wwhf-2024-badge/ghidra-flag-1-bytes.webp" alt="Ghidra listing with the Flag 1 string spread out one character every four bytes" decoding="async" loading="lazy" width="495" height="699"></p>
<p>It is still hard to pull out the string on this but if you scroll down you will see your string with a message.</p>
<h5 id="binary-ninja">Binary Ninja</h5>
<p>This is not the best for decompilation, but strings are on point.</p>
<p><img src="/writeups/wwhf-2024-badge/binja-strings-flag-1.webp" alt="Binary Ninja strings view showing the Flag 1 string" decoding="async" loading="lazy" width="326" height="66"></p>
<h5 id="strings">Strings</h5>
<p>To show all strings</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">strings firmware.bin
</code></pre></div>
<p>You can filter down on URL</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">strings wwhf2024.bin | grep <span class="hljs-string">"\.com"</span> -A 4
</code></pre></div>
<p>Or even use bulk extractor to get more info out of the binary</p>
<h3 id="deadwood-flag-2">Deadwood Flag 2<a class="heading-anchor" href="#deadwood-flag-2" aria-label="Copy link to “Deadwood Flag 2”">#</a></h3>
<p>This is the serial message that will appear after the ASCII art. It will take a while for it to pop up.</p>
<details>
<summary>Serial Output</summary>
<div class="code-block code-block--collapsible"><input type="checkbox" id="code-more-2" class="code-block__toggle"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">[BADGE] Loading badge config...
[BADGE] Loading bounties...
  - Vendors: 26
  - Staff: 2
  - Tracks: 4
  - Badge: 4
  - SpecialEvents: 4
[BADGE] WWHF Badge v1.26
[BADGE] 24:58:7C:F4:8E:30
[WiFi] Wi-Fi ready
[WiFi] Station mode started
[WiFi] Sta[WiFi] Station disconnected from AP
[WiFi] Station disconnected from AP
[WiFi] Station disconnected from AP
[WiFi] Station disconnected from AP
[WiFi] Station disconnected from AP
[WiFi] Station disconnected from AP
[WiFi] Station disconnected from AP
[WiFi] Station disconnected from AP
[WiFi] Station disconnected from AP
[WiFi] Station disconnected from AP
[WiFi] Station connected to AP
[WiFi] Station got IP address
[WiFi] Registering device with API server
[BADGE] Writing badge config...
[BADGE] Boounties: Staff 2 | Event 4 | Track 4 | Vendor 26 | Badge 4
[BADGE] Writing bounty tracker...
[WiFi] Registration success
[MQTT] Connecting to MQTT Broker as WrangleCelestial3...
[MQTT] Connected to MQTT broker
[MQTT] Subscribed to broadcast messages
[MQTT] Published registration messages
[BADGE] Added Voltage to index 28: 3.90 volts (59%) [Avg: 3.86 volts (52%)]
[BADGE] Added Voltage to index 29: 3.90 volts (61%) [Avg: 3.86 volts (52%)]
[BADGE] Added Voltage to index 30: 3.90 volts (59%) [Avg: 3.86 volts (52%)]
                           _╓▄▓▓▓╬╩^``▒_,░µ_ _
                         ,▓▓╬╢╩`╓Ü@D╚╩``╙╙╩Ü=_-╔___
                       ╓▓▓█╬╬Ü#R`Ü `         '|»░`░_
                      ║▓█▓╬▒░░Ü ````           `╙\²
                      ▓█▓█▓╬Ü`                   '
                     ▓█▓███╬Ü
                    ▐█▓▓██╬╩H
                    ║█▓██▓╬_
                    '▓▓▓██▓▒╓_   '    ___
                     █▓▓██╩▀▀▓▄▓_   _______
                     ▀║▓██▓▓Ö╠╬█▌,,`╠╠╩▓DHφ_  __,,,╔▄
                   '▀%╣▓█╣╠╬▒╠█▓  \_ ╠==      `      j
                    ' ║██▓╬▒╣███       ``    ,
                     ║▓▓╬╣╫╫▓██▒    `ⁿq∩
                     ╠▓█╣╬║▓███▓_   _  '_
                     ║▓▓█▓▓██████▒_
                     ╠▓█████▌╬▒╬Ü▒__╓_,_
                    |║▓█▓██╬███╬▀╙^`````╚▒
                     [╣█▓█╬╣▓█▒▒∩▒__
                     ╙╠▓▓╬╬╣██╣▒_╙`                     .╓╖__
                    ⁿ^╠║╬╠╬╩║╝Ñ╬▒__                      ╣▓▓███▓▄╖,_
                     |▒╩╣╬]╠ÜÜ╠╬╬R@╓╦∩                ,▓█████▓█▓▓▓▓▓██▓▄,
                     `╔╬╬╣▒╩▒▒∩Ü╠Ü║▒¼               _╣▓████▓█▓▓███▓▓▓▓▓▓▓██▄,
                     ,▄╣╬╬Ñ╬▒╬╬╩ÜÜ╩╩                ╔▓▓█▓▓▓▓▓▓▓█▓█▓█▓██▓▓▓▓▓▓██▄
                _╓▄██▓╬╬╠▒^ÜÅ╚╙▒[ÜÄ               ,╔▓█▓█▓▓▓▓▓▓█▓▓▓███▓█▓▓▓▓▓▓▓▓▓
             ,▄███████╬╬╬░Ü╙²-   ╠_  ,          ▄▄▓▓█▓██▓▓▓█▓▓▓▓▓▓██▓▓██▓▓█▓▓▓▓▓
           ▄████████████▓▒^▒UÜ _jÜ`=_        µ▓▓█▓█████▓█▓▓█▓▓▓▓██▓██▓█▓█▓▓▓▓▓█▓
         ╔█████████████████▓▓@▄▒╔╠╗▄,_╓-__╒,╦╬█████████████▓▓▓▓▓█▓▓▓▓█▓▓▓▓▓▓███▓
        ╔██████████████████████████████▓██▓▓████████▓████▓▓▓█▓██▓█▓▓██▓█▓▓█▓█▓██

        I2luY2x1ZGUgPHN0ZGlvLmg+CiNpbmNsdWRlIDxzdHJpbmcuaD4KI2luY2x1ZGUgPHN0ZGxpYi5oPgoKdm9pZCByKGNoYXIgKnMsIGludCBzaGlmdCkgewogICAgZm9yIChpbnQgaSA9IDA7IHNbaV07IGkrKykgewogICAgICAgIGlmIChzW2ldID49ICdBJyAmJiBzW2ldIDw9ICdaJykgc1tpXSA9ICgoc1tpXSAtICdBJyArIHNoaWZ0KSAlIDI2KSArICdBJzsKICAgICAgICBlbHNlIGlmIChzW2ldID49ICdhJyAmJiBzW2ldIDw9ICd6Jykgc1tpXSA9ICgoc1tpXSAtICdhJyArIHNoaWZ0KSAlIDI2KSArICdhJzsKICAgIH0KfQoKaW50IG1haW4oaW50IGFyZ2MsIGNoYXIgKmFyZ3ZbXSkgewogICAgaWYgKGFyZ2MgIT0gMikgcmV0dXJuIDE7CiAgICBpbnQgc2hpZnQgPSBhdG9pKGFyZ3ZbMV0pOwogICAgY2hhciBmW10gPSAiRlZUe3JMcl90MF8zaTNlbGp1M2UzfSI7CiAgICByKGYsIHNoaWZ0KTsKICAgIHByaW50ZigiJXNcbiIsIGYpOwogICAgcmV0dXJuIDA7Cn0K
[BADGE] Added Voltage to index 31: 3.93 volts (65%) [Avg: 3.86 volts (53%)]
</code></pre><label for="code-more-2" class="code-block__more"><span class="code-block__more-closed">Show all 67 lines</span><span class="code-block__more-open">Show less</span></label></div>
</details>
<h4 id="extraction-of-string">Extraction of string<a class="heading-anchor" href="#extraction-of-string" aria-label="Copy link to “Extraction of string”">#</a></h4>
<p>Extracting the base64 leaves us with</p>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">I2luY2x1ZGUgPHN0ZGlvLmg+CiNpbmNsdWRlIDxzdHJpbmcuaD4KI2luY2x1ZGUgPHN0ZGxpYi5oPgoKdm9pZCByKGNoYXIgKnMsIGludCBzaGlmdCkgewogICAgZm9yIChpbnQgaSA9IDA7IHNbaV07IGkrKykgewogICAgICAgIGlmIChzW2ldID49ICdBJyAmJiBzW2ldIDw9ICdaJykgc1tpXSA9ICgoc1tpXSAtICdBJyArIHNoaWZ0KSAlIDI2KSArICdBJzsKICAgICAgICBlbHNlIGlmIChzW2ldID49ICdhJyAmJiBzW2ldIDw9ICd6Jykgc1tpXSA9ICgoc1tpXSAtICdhJyArIHNoaWZ0KSAlIDI2KSArICdhJzsKICAgIH0KfQoKaW50IG1haW4oaW50IGFyZ2MsIGNoYXIgKmFyZ3ZbXSkgewogICAgaWYgKGFyZ2MgIT0gMikgcmV0dXJuIDE7CiAgICBpbnQgc2hpZnQgPSBhdG9pKGFyZ3ZbMV0pOwogICAgY2hhciBmW10gPSAiRlZUe3JMcl90MF8zaTNlbGp1M2UzfSI7CiAgICByKGYsIHNoaWZ0KTsKICAgIHByaW50ZigiJXNcbiIsIGYpOwogICAgcmV0dXJuIDA7Cn0K
</code></pre></div>
<p>which run through From Base64 in <a href="https://gchq.github.io/CyberChef/#recipe=From_Base64(&#x27;A-Za-z0-9%2B/%3D&#x27;,true,false)&#x26;input=STJsdVkyeDFaR1VnUEhOMFpHbHZMbWcrQ2lOcGJtTnNkV1JsSUR4emRISnBibWN1YUQ0S0kybHVZMngxWkdVZ1BITjBaR3hwWWk1b1Bnb0tkbTlwWkNCeUtHTm9ZWElnS25Nc0lHbHVkQ0J6YUdsbWRDa2dld29nSUNBZ1ptOXlJQ2hwYm5RZ2FTQTlJREE3SUhOYmFWMDdJR2tyS3lrZ2V3b2dJQ0FnSUNBZ0lHbG1JQ2h6VzJsZElENDlJQ2RCSnlBbUppQnpXMmxkSUR3OUlDZGFKeWtnYzF0cFhTQTlJQ2dvYzF0cFhTQXRJQ2RCSnlBcklITm9hV1owS1NBbElESTJLU0FySUNkQkp6c0tJQ0FnSUNBZ0lDQmxiSE5sSUdsbUlDaHpXMmxkSUQ0OUlDZGhKeUFtSmlCelcybGRJRHc5SUNkNkp5a2djMXRwWFNBOUlDZ29jMXRwWFNBdElDZGhKeUFySUhOb2FXWjBLU0FsSURJMktTQXJJQ2RoSnpzS0lDQWdJSDBLZlFvS2FXNTBJRzFoYVc0b2FXNTBJR0Z5WjJNc0lHTm9ZWElnS21GeVozWmJYU2tnZXdvZ0lDQWdhV1lnS0dGeVoyTWdJVDBnTWlrZ2NtVjBkWEp1SURFN0NpQWdJQ0JwYm5RZ2MyaHBablFnUFNCaGRHOXBLR0Z5WjNaYk1WMHBPd29nSUNBZ1kyaGhjaUJtVzEwZ1BTQWlSbFpVZTNKTWNsOTBNRjh6YVRObGJHcDFNMlV6ZlNJN0NpQWdJQ0J5S0dZc0lITm9hV1owS1RzS0lDQWdJSEJ5YVc1MFppZ2lKWE5jYmlJc0lHWXBPd29nSUNBZ2NtVjBkWEp1SURBN0NuMEsNCg&#x26;ieol=CRLF">CyberChef</a> gives us</p>
<div class="code-block" data-lang="c"><div class="code-block__bar"><span class="code-block__lang">c</span></div><pre><code class="hljs language-c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;stdio.h></span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;string.h></span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;stdlib.h></span></span>

<span class="hljs-type">void</span> <span class="hljs-title function_">r</span><span class="hljs-params">(<span class="hljs-type">char</span> *s, <span class="hljs-type">int</span> shift)</span> {
    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; s[i]; i++) {
        <span class="hljs-keyword">if</span> (s[i] >= <span class="hljs-string">'A'</span> &#x26;&#x26; s[i] &#x3C;= <span class="hljs-string">'Z'</span>) s[i] = ((s[i] - <span class="hljs-string">'A'</span> + shift) % <span class="hljs-number">26</span>) + <span class="hljs-string">'A'</span>;
        <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (s[i] >= <span class="hljs-string">'a'</span> &#x26;&#x26; s[i] &#x3C;= <span class="hljs-string">'z'</span>) s[i] = ((s[i] - <span class="hljs-string">'a'</span> + shift) % <span class="hljs-number">26</span>) + <span class="hljs-string">'a'</span>;
    }
}

<span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span> *argv[])</span> {
    <span class="hljs-keyword">if</span> (argc != <span class="hljs-number">2</span>) <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
    <span class="hljs-type">int</span> shift = atoi(argv[<span class="hljs-number">1</span>]);
    <span class="hljs-type">char</span> f[] = <span class="hljs-string">"FVT{rLr_t0_3i3elju3e3}"</span>;
    r(f, shift);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%s\n"</span>, f);
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre></div>
<p>This appears to be a simple Caesar cipher. Using the <a href="https://gchq.github.io/CyberChef/#recipe=ROT13_Brute_Force(true,true,false,100,0,true,&#x27;&#x27;)&#x26;input=RlZUe3JMcl90MF8zaTNlbGp1M2UzfQ&#x26;ieol=CRLF">CyberChef</a> ROT13 brute force we can see an option that makes sense.</p>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">Amount =  1: GWU{sMs_u0_3j3fmkv3f3}
Amount =  2: HXV{tNt_v0_3k3gnlw3g3}
Amount =  3: IYW{uOu_w0_3l3homx3h3}
Amount =  4: JZX{vPv_x0_3m3ipny3i3}
Amount =  5: KAY{wQw_y0_3n3jqoz3j3}
Amount =  6: LBZ{xRx_z0_3o3krpa3k3}
Amount =  7: MCA{ySy_a0_3p3lsqb3l3}
Amount =  8: NDB{zTz_b0_3q3mtrc3m3}
Amount =  9: OEC{aUa_c0_3r3nusd3n3}
Amount = 10: PFD{bVb_d0_3s3ovte3o3}
Amount = 11: QGE{cWc_e0_3t3pwuf3p3}
Amount = 12: RHF{dXd_f0_3u3qxvg3q3}
Amount = 13: SIG{eYe_g0_3v3rywh3r3}
Amount = 14: TJH{fZf_h0_3w3szxi3s3}
Amount = 15: UKI{gAg_i0_3x3tayj3t3}
Amount = 16: VLJ{hBh_j0_3y3ubzk3u3}
Amount = 17: WMK{iCi_k0_3z3vcal3v3}
Amount = 18: XNL{jDj_l0_3a3wdbm3w3}
Amount = 19: YOM{kEk_m0_3b3xecn3x3}
Amount = 20: ZPN{lFl_n0_3c3yfdo3y3}
Amount = 21: AQO{mGm_o0_3d3zgep3z3}
Amount = 22: BRP{nHn_p0_3e3ahfq3a3}
Amount = 23: CSQ{oIo_q0_3f3bigr3b3}
Amount = 24: DTR{pJp_r0_3g3cjhs3c3}
Amount = 25: EUS{qKq_s0_3h3dkit3d3}
</code></pre></div>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">SIG{eYe_g0_3v3rywh3r3}
</code></pre></div>
<h4 id="alternate-method">Alternate Method<a class="heading-anchor" href="#alternate-method" aria-label="Copy link to “Alternate Method”">#</a></h4>
<p>We can also find this large block of text through strings. Then follow similar methods to above.</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">strings wwhf2024.bin | awk <span class="hljs-string">'length($0) > 200'</span> | <span class="hljs-built_in">base64</span> -d
</code></pre></div>
<h3 id="deadwood-flag-3">Deadwood Flag 3<a class="heading-anchor" href="#deadwood-flag-3" aria-label="Copy link to “Deadwood Flag 3”">#</a></h3>
<p>The LED in the bottom right will sometimes burst in a weird pattern. Setting up a YouTube live stream to catch this we can create a <a href="https://youtube.com/shorts/KFdoEGLke7U?feature=share">video</a>.</p>
<p>Decoding leaves us with</p>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">.... .- -.-. -.- - .... . ... .. --. -. .- .-.. ...
</code></pre></div>
<p>Putting this into English gives us</p>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">hackthesignals
</code></pre></div>
<h3 id="deadwood-flag-4">Deadwood Flag 4<a class="heading-anchor" href="#deadwood-flag-4" aria-label="Copy link to “Deadwood Flag 4”">#</a></h3>
<p>This one is very tricky but is the decryption of the mystery signal. If you monitored MQTT long enough you might have seen a mystery signal event pop up. In the data field is an encrypted blob that contains the flag.</p>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">{
  "broadcast_event": 12,
  "data": "WfFIAmqjR7RjIJ8fsPdkeUKIUxqrkeZSk9G2+rUKtVv5H1XOmrEWpZjyRUtZVL2AXp4yRFwLkc4C9MgnBwarOA==",
  "date": "10/09/24",
  "time": "05:51PM UTC",
  "message": "Mystery Signal"
}
</code></pre></div>
<p>We know that the badge can decrypt the message so we need some way to send it a message so the decryption key can be in memory. We made the badge get extra points by sending it a scan message. (<a href="#how-the-scanners-report-a-scan">see Extra for how this was done</a>)</p>
<p>When we are ready to download memory we do</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">python -m esptool dump_mem 0x40380000 393216 out.bin
</code></pre></div>
<p>We choose this memory section based on the ESP32 <a href="https://dl.espressif.com/public/esp32s3-mm.pdf">docs</a>. (S3 mem sections from @precurse)</p>
<p>To extract all AES keys that are the size we are looking for</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">strings out.bin | awk <span class="hljs-string">'length($0) == 16'</span>
</code></pre></div>
<p>Using the key to decrypt we get the message. <a href="https://gchq.github.io/CyberChef/#recipe=From_Base64(&#x27;A-Za-z0-9%2B/%3D&#x27;,true,false)AES_Decrypt(%7B&#x27;option&#x27;:&#x27;Hex&#x27;,&#x27;string&#x27;:&#x27;36336135666435393638386530346137&#x27;%7D,%7B&#x27;option&#x27;:&#x27;Hex&#x27;,&#x27;string&#x27;:&#x27;&#x27;%7D,&#x27;ECB&#x27;,&#x27;Raw&#x27;,&#x27;Raw&#x27;,%7B&#x27;option&#x27;:&#x27;Hex&#x27;,&#x27;string&#x27;:&#x27;&#x27;%7D,%7B&#x27;option&#x27;:&#x27;Hex&#x27;,&#x27;string&#x27;:&#x27;&#x27;%7D)From_Hex(&#x27;Auto&#x27;)&#x26;input=V2ZGSUFtcWpSN1JqSUo4ZnNQZGtlVUtJVXhxcmtlWlNrOUcyK3JVS3RWdjVIMVhPbXJFV3BaanlSVXRaVkwyQVhwNHlSRndMa2M0QzlNZ25Cd2FyT0E9PQ">CyberChef</a></p>
<h2 id="2025-mile-high-flags">2025 Mile High Flags<a class="heading-anchor" href="#2025-mile-high-flags" aria-label="Copy link to “2025 Mile High Flags”">#</a></h2>
<h3 id="mile-high-flag-1">Mile High Flag 1<a class="heading-anchor" href="#mile-high-flag-1" aria-label="Copy link to “Mile High Flag 1”">#</a></h3>
<ol>
<li>Turn on the badge and record the light flashes (<a href="https://youtube.com/shorts/nDjW8srws28?feature=share">video uploaded here</a>)</li>
<li>Transcribe the 8 LEDs to binary strings</li>
</ol>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">01010111
01000111
01001110
01111011
01110111
00110100
01101110
01110100
00110011
01100100
01011111
01100100
00110011
00110100
01100100
01011111
00110000
01110010
01011111
00110100
01101100
00110001
01110110
00110011
</code></pre></div>
<ol start="3">
<li>CyberChef Decode (<a href="https://gchq.github.io/CyberChef/#recipe=From_Binary(&#x27;None&#x27;,8)&#x26;input=MDEwMTAxMTENCjAxMDAwMTExDQowMTAwMTExMA0KMDExMTEwMTENCjAxMTEwMTExDQowMDExMDEwMA0KMDExMDExMTANCjAxMTEwMTAwDQowMDExMDAxMQ0KMDExMDAxMDANCjAxMDExMTExDQowMTEwMDEwMA0KMDAxMTAwMTENCjAwMTEwMTAwDQowMTEwMDEwMA0KMDEwMTExMTENCjAwMTEwMDAwDQowMTExMDAxMA0KMDEwMTExMTENCjAwMTEwMTAwDQowMTEwMTEwMA0KMDAxMTAwMDENCjAxMTEwMTEwDQowMDExMDAxMQ&#x26;ieol=CRLF&#x26;oeol=CRLF">LINK</a>)</li>
</ol>
<h3 id="mile-high-flag-2">Mile High Flag 2<a class="heading-anchor" href="#mile-high-flag-2" aria-label="Copy link to “Mile High Flag 2”">#</a></h3>
<p>This is the serial message that will appear after the ASCII art. It will take a while for it to pop up.</p>
<details>
<summary>Serial Output</summary>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">                           _╓▄▓▓▓╬╩^``▒_,░µ_ _
                         ,▓▓╬╢╩`╓Ü@D╚╩``╙╙╩Ü=_-╔___
                       ╓▓▓█╬╬Ü#R`Ü `         '|»░`░_
                      ║▓█▓╬▒░░Ü ````           `╙\²
                      ▓█▓█▓╬Ü`                   '
                     ▓█▓███╬Ü
                    ▐█▓▓██╬╩H
                    ║█▓██▓╬_
                    '▓▓▓██▓▒╓_   '    ___
                     █▓▓██╩▀▀▓▄▓_   _______
                     ▀║▓██▓▓Ö╠╬█▌,,`╠╠╩▓DHφ_  __,,,╔▄
                   '▀%╣▓█╣╠╬▒╠█▓  \_ ╠==      `      j
                    ' ║██▓╬▒╣███       ``    ,
                     ║▓▓╬╣╫╫▓██▒    `ⁿq∩
                     ╠▓█╣╬║▓███▓_   _  '_
                     ║▓▓█▓▓██████▒_
                     ╠▓█████▌╬▒╬Ü▒__╓_,_
                    |║▓█▓██╬███╬▀╙^`````╚▒
                     [╣█▓█╬╣▓█▒▒∩▒__
                     ╙╠▓▓╬╬╣██╣▒_╙`                     .╓╖__
                    ⁿ^╠║╬╠╬╩║╝Ñ╬▒__                      ╣▓▓███▓▄╖,_
                     |▒╩╣╬]╠ÜÜ╠╬╬R@╓╦∩                ,▓█████▓█▓▓▓▓▓██▓▄,
                     `╔╬╬╣▒╩▒▒∩Ü╠Ü║▒¼               _╣▓████▓█▓▓███▓▓▓▓▓▓▓██▄,
                     ,▄╣╬╬Ñ╬▒╬╬╩ÜÜ╩╩                ╔▓▓█▓▓▓▓▓▓▓█▓█▓█▓██▓▓▓▓▓▓██▄
                _╓▄██▓╬╬╠▒^ÜÅ╚╙▒[ÜÄ               ,╔▓█▓█▓▓▓▓▓▓█▓▓▓███▓█▓▓▓▓▓▓▓▓▓
             ,▄███████╬╬╬░Ü╙²-   ╠_  ,          ▄▄▓▓█▓██▓▓▓█▓▓▓▓▓▓██▓▓██▓▓█▓▓▓▓▓
           ▄████████████▓▒^▒UÜ _jÜ`=_        µ▓▓█▓█████▓█▓▓█▓▓▓▓██▓██▓█▓█▓▓▓▓▓█▓
         ╔█████████████████▓▓@▄▒╔╠╗▄,_╓-__╒,╦╬█████████████▓▓▓▓▓█▓▓▓▓█▓▓▓▓▓▓███▓
        ╔██████████████████████████████▓██▓▓████████▓████▓▓▓█▓██▓█▓▓██▓█▓▓█▓█▓██
        JVNJAAADAAAAABAAAAAP77YAAC4AAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAOD65A4AFUBHGSDOABJTGSCVDINFZSA4DSN5TXEYLNEBRWC3TON52CAYTFEBZHK3RANFXCARCPKMQG233EMUXA2DIKEQAAAAAAAAAAAUCFAAAEYAIDABZYFCHUAAAAAAAAAAAABYAAEIAAWAJQAAAAQAAAAAEAAAAAAAAABKRHAAAAAIAAAAAEAAAAAAAEAAAAEAAAAAACAAAAIAAAAAAAAAAAAQAAAAAAAAAAAAEAAAAAAAQAAAAAAAAAAMAGBBIAAAIAAAAQAAAAAAAQAAABAAAAAAAAAAAQAAAAAAAAAAAAAAAAABMCOAAAJ4AAAAAAIAAAAKAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAYAAAACECMAAAKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAAAAAAAAAAAAAABAQAAACIAAAAAAAAAAAAAAAAAAXHIZLYOQAAAAFQA4AAAABAAAAAACAAAAAAEAAAAAAAAAAAAAAAAAAAAAACAAAAMAXHE43SMMAAAABIAUAAAACAAAAAABQAAAAAUAAAAAAAAAAAAAAAAAAAAAAEAAAAIAXHEZLMN5RQAAAMAAAAAADAAAAAAAQAAAABAAAAAAAAAAAAAAAAAAAAAAAEAAAAIIAAAAAAAAAAAAAAAAAAAAAAAAAIYJYAAAAAAAAAJAAAAAACAACQBPBAAAAMYBIAAAAQAAAAAIAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGMAEABEAAAAAAEAAAEIAPYAQAAAEN4HAAAAKRUIQAAABBILAWKYXAADAO7QBAAAAIB3PB4AAACQ5MHIZ2AAHC5MAWB36AEAAABDPBYAAACX6AQGAQLOYAZZRAAAABIGSWAAJFIFAAKRCAIUBCAAABIACULTSAEAAA4EAAEAAABBKIJJUUQQBAAAQAAAAAAAAYAAAAB3DILRQFYZTAMZRHEAAAAAAAUAGYAAAADWACAAAEN7AAACYAIAAAQACAAACGU3UOJUW4Z3TAAAAAAEYAQAAALAAAAACGVKTADCAIAAACAAAAABDI5KUSRAAAAANIBAAAD4AAAAAENBGY33CAAAAAAAAAAAAEAAAAFLRIAQABEAAAAAA7IATGAAWAAAACAAAAAIQAAAAAIAAAAABAAAAABAAAAABCAAAAAGQAAAAAEAAAAABAAAAAAIAAAAAAAFJAEAQAAAAAAAAMAHOAD7ACBQALEA74AIGAAPAB2YBB4AB4AQAAADAAWYAO4AQMACAAHDQCBQAW4AMOAIGAB2ABRYBAYAJCAGHAEDAADQBY4AQMACEADDQCBQA2YAP4AIGAATQD7QBAYADEAH6AEDAANYCXMAQMAERAG5QCBQA3EA3WAIAAAAAAAIAAAAAAAABAAAQAAAACAALGAIAAA6QAAIAAEABCAAKAA5AAUBAAAAAAAERACRQCPIAAEAKIIAAAAAABEIAYIAUCAABACTSAAAAAAAIMGG6AEDAAAIAWAQAAAAAACIRRZABIEAACAAJADPACAIACEAN4AIGAAMQBXQBBIACSAG6AEIAAMIA3YARAABZADPACEAAIEAN4AIQABEQBXQBCAAFCAG6AEIAAWIA3YARAADBADPACAIANEAN4AIVABYQBXQBCUAICAEYAERABAIAFUBCMAEBADPACKYAPEAN4AIGAATQAWYA5YAC4AALABCQALQACMAE4ABOAANQA3IAFYACGADWAAXAAKYAWQAC4ABTAC7QALQAHMAMUABOABBQBVYAFYAEWAFUAAXAAUYAWQAEGADDADRAAQYANMAOQAA2AACIAAAAAEAAAAAAAAAAAAAAAAAAAAFDAEAAACIAAAAAAAAAAAAAAAAAGEAA6AAAAAAAAAAAAA6E233EOVWGKPQAMRQXIYIAKN4XG5DFNUXFE5LOORUW2ZIAIRSWE5LHM5QWE3DFIF2HI4TJMJ2XIZIAJZ2WY3DBMJWGKQLUORZGSYTVORSQAQLTONSW2YTMPFKGS5DMMVAXI5DSNFRHK5DFABKGC4THMV2EM4TBNVSXO33SNNAXI5DSNFRHK5DFABAXG43FNVRGY6KGNFWGKVTFOJZWS33OIF2HI4TJMJ2XIZIAIFZXGZLNMJWHSSLOMZXXE3LBORUW63TBNRLGK4TTNFXW4QLUORZGSYTVORSQAQLTONSW2YTMPFBW63TGNFTXK4TBORUW63SBOR2HE2LCOV2GKACSMVTFGYLGMV2HSUTVNRSXGQLUORZGSYTVORSQAQ3PNVYGS3DBORUW63SSMVWGC6DBORUW63TTIF2HI4TJMJ2XIZIAIFZXGZLNMJWHSUDSN5SHKY3UIF2HI4TJMJ2XIZIAJZ2WY3DBMJWGKQ3PNZ2GK6DUIF2HI4TJMJ2XIZIAIFZXGZLNMJWHSQ3PNVYGC3TZIF2HI4TJMJ2XIZIAKJ2W45DJNVSUG33NOBQXI2LCNFWGS5DZIF2HI4TJMJ2XIZIAKN4XG5DFNUXFE5LOORUW2ZJOKZSXE43JN5XGS3THABJXI4TJNZTQAZ3FORPUYZLOM52GQACUOJQWS3AAKRZGC2LMFZSGY3AAKBZG6Z3SMFWQAU3ZON2GK3IAJVQWS3QAKN4XG5DFNUXFEZLGNRSWG5DJN5XAAQ3IMFZAALTDORXXEABOMNRXI33SABJXS43UMVWS4RDJMFTW433TORUWG4YAKN4XG5DFNUXFE5LOORUW2ZJOINXW24DJNRSXEU3FOJ3GSY3FOMAEIZLCOVTWO2LOM5GW6ZDFOMAGOZLUL5BWQYLSOMAE6YTKMVRXIAAAAAACSUAAIAAESAD4ABQAANYANMAGGACYAB2QA4QAGIAG6ACYABQQANAAOEADIADVAB5AAAAAJS4BZ2CCTJQUTKJVWCEJLULXNEAAIIABAEEAGIAAAECSAAIBCEIQIIABAEHAIIABAECQOBYEDUBQQAQOAMQAACAEEAAQGCAFEAAQCHIDBCYD6X37CHKQUOQCAYHAGAAABYBQAAABBAAQACAAAAAAAAA6AEAACACUAILFO4TBOBHG63SFPBRWK4DUNFXW4VDIOJXXO4YBBAAQABYBAAAAAAB5AEABQLSOIVKEG33SMVAXA4BMKZSXE43JN5XD25RZFYYACACUBYKEM4TBNVSXO33SNNCGS43QNRQXSTTBNVSQQLSOIVKCAOJOGAFACAAFKRZGC2LMAAAAUAIAAVCGKYTVM4AAADABAADTCLRQFYYC4MAAAAFACAAFGEXDALRQAAAAKAIAAEAAABIBAAAAAAAIAEAAWAAAAAAAAAAAAAAABK74USRQAAKNKABAAAAAKUAAAAG4EYAABXAIAAAAAAAAAAAAAAAAAEAAAAATAAAAAJYAAAADCJYAAAYQSAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAUSTIRJR2K22BJA324CCRU4SZVEQ6NY7YAIAAAAEGOS4KVZWK4TTLRGWS23FEBDGK3DDNBOEIZLTNN2G64C4KRZGC2LMLRXWE2S4IRSWE5LHLRXGK5BZFYYFYVDSMFUWYLTQMRRAAU2IIEZDKNQADUVVUCSBXVYEEDJZFTKJB43R7SV7ZJBDEP2X432CDQOMMKOWDYEIAJYAAAAAAAAAAAAAAAE2E4AAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIYJYAAAAAAAAAAAAAAAAAABPUG33SIV4GKTLBNFXAA3LTMNXXEZLFFZSGY3AAAAAAAAH7EUACAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAIAAAAAEAAABAAYAAAAAUAAACAAAAAAAAAAAAAAAAAAAAAAAAAQAAIAAAADQAAAQAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAQAAAANAAABAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAABIAMAABECAAAAJQAQAAAAAAAAAAAAAAAEYAI2AAAAAKYAFGAC7ABLAARIAKIAFGACJABHQATQAL4AESACOABDAATYAAAAAAAF5ATX74AAAAEAAAAABAAAAAAAAAAAACAAAAAAAAPYAAAAAAAAAAACAAAAAAEAAAAAAAAAAAAAAAAAAAAAAABCAAAAAAEAFMADBABZAARQANEAGYADFABEQA3QAMYAG6AAAAAAAAJAAAQAAAACUABZAAYIANYAHGADMABQQA5AANEAG6ADOAAAAAAAAAAALABHYAEAAAAIAKMAHIADSABUQA3QAM4AEMADJABWAAZIAJEAG4ADGABXQAAAA2QAQAAABAAYAAMAAGAADAABQAA2AAYQAGAAAAABMAADAAAIAIMAG6ADNABYAAYIANYAHSACOABQQA3IAMUAAAAAAABKAA4QAMEAGSADMAAAAANAAAYAACACGABUQA3AAMUAEIADFABZQAYYAOIAGSADQAB2AA2IAN4AG4AAAAAAAAVAAOIAGCADJABWAAAAAGAAAQAABABDAA2IANQAGKACWABSQA4QAOMAGSADPABXAAAAAAAADCABOAAYAALQAGAAC4ABQAAAAANAABIAACACJABXAA5AAMUAHEADOABQQA3AAJYAGCADNABSQAAAAKQAHEADBABUQA3AAFYAGIADMABWAAAAAFAAAEAABABGAAZIAM4AGCADMABBQA3YAOAAHSADSABUQAZYANAAHIAAAAAQAAAAAHQAAUAABABHQA4QANEAGOADJABXAAYIANQAEMADJABWAAZIANYAGCADNABSQAAAAKQAHEADBABUQA3AAFYAGIADMABWAAAAAFQAAMAABABIAA4QAN4AGIADVABRQA5AAJYAGCADNABSQAAAAAAAFIADSABQQA2IANQAAAABQAADAAAIAKAAHEADPABSAA5IAMMAHIACWABSQA4QAOMAGSADPABXAAAAAGEAC4ABQAAXAAMAAAAADQAAIAAAQAQIAOMAHGADFABWQAYQANQAHSABAABLAAZIAOIAHGADJABXQA3QAAAADCABOAAYAALQAGAAC4ABQAAAAAOCDAAAOUAIAAAAAAAAAAAAAAAHPXO7TYP3YNVWCA5TFOJZWS33OHURDCLRQEIQGK3TDN5SGS3THHURFKVCGFU4CEIDTORQW4ZDBNRXW4ZJ5EJ4WK4ZCH47A2CQNBI6GC43TMVWWE3DZEB4G23DOOM6SE5LSNY5HGY3IMVWWC4ZNNVUWG4TPONXWM5BNMNXW2OTBONWS45RREIQG2YLONFTGK43UKZSXE43JN5XD2IRRFYYCEPQNBIQCAPDBONZWK3LCNR4USZDFNZ2GS5DZEB3GK4TTNFXW4PJCGEXDALRQFYYCEIDOMFWWKPJCJV4UC4DQNRUWGYLUNFXW4LTBOBYCELZ6BUFCAIB4ORZHK43UJFXGM3ZAPBWWY3TTHURHK4TOHJZWG2DFNVQXGLLNNFRXE33TN5THILLDN5WTUYLTNUXHMMRCHYGQUIBAEAQDY43FMN2XE2LUPE7A2CRAEAQCAIBAHRZGK4LVMVZXIZLEKBZGS5TJNRSWOZLTEB4G23DOOM6SE5LSNY5HGY3IMVWWC4ZNNVUWG4TPONXWM5BNMNXW2OTBONWS45RTEI7A2CRAEAQCAIBAEAQDY4TFOF2WK43UMVSEK6DFMN2XI2LPNZGGK5TFNQQGYZLWMVWD2ITBONEW45TPNNSXEIRAOVUUCY3DMVZXGPJCMZQWY43FEIXT4DIKEAQCAIBAEA6C64TFOF2WK43UMVSFA4TJOZUWYZLHMVZT4DIKEAQCAIB4F5ZWKY3VOJUXI6J6BUFCAIB4F52HE5LTOREW4ZTPHYGQUPBPMFZXGZLNMJWHSPQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAABQAAAAFMG4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA===
</code></pre></div>
</details>
<h4 id="convert-message">Convert message<a class="heading-anchor" href="#convert-message" aria-label="Copy link to “Convert message”">#</a></h4>
<p>Using CyberChef with Magic we can see that this is an exe and download it.</p>
<p><img src="/writeups/wwhf-2024-badge/cyberchef-base32-exe.webp" alt="CyberChef From Base32 decoding the serial blob into a .NET executable" decoding="async" loading="lazy" width="1003" height="1280"></p>
<p>Seeing some of the strings output led me to believe that it is a C# application. We could verify that with Detect It Easy, but dotPeek can quickly tell us also.</p>
<h4 id="dotpeek">dotPeek<a class="heading-anchor" href="#dotpeek" aria-label="Copy link to “dotPeek”">#</a></h4>
<p>I used dotPeek to analyze the binary, but ILSpy and dnSpy (my favorite) work just as well.</p>
<p><img src="/writeups/wwhf-2024-badge/dotpeek-trail-program.webp" alt="dotPeek showing the decompiled Trail program and its encoded data string" decoding="async" loading="lazy" width="1054" height="312"></p>
<details>
<summary>Decompiled code</summary>
<div class="code-block" data-lang="csharp"><div class="code-block__bar"><span class="code-block__lang">csharp</span></div><pre><code class="hljs language-csharp"><span class="hljs-comment">// Decompiled with JetBrains decompiler</span>
<span class="hljs-comment">// Type: Program</span>
<span class="hljs-comment">// Assembly: Trail, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</span>
<span class="hljs-comment">// MVID: E81CB84C-9A42-4961-A935-B08895D17769</span>
<span class="hljs-comment">// Assembly location: C:\Users\NSSte\Downloads\download.exe</span>

<span class="hljs-meta">#nullable enable</span>
<span class="hljs-keyword">internal</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Program</span>
{
  <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-built_in">string</span> data = <span class="hljs-string">"P@I|`7kcXur2oXa4q4uz"</span>;

  <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-built_in">string</span> <span class="hljs-title">Trail</span>()</span>
  {
    <span class="hljs-built_in">char</span>[] chArray = <span class="hljs-keyword">new</span> <span class="hljs-built_in">char</span>[Program.data.Length];
    <span class="hljs-keyword">for</span> (<span class="hljs-built_in">int</span> index = <span class="hljs-number">0</span>; index &#x3C; Program.data.Length; ++index)
      chArray[index] = (<span class="hljs-built_in">char</span>) ((<span class="hljs-built_in">uint</span>) Program.data[index] ^ <span class="hljs-number">7U</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">string</span>(chArray);
  }

  <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Main</span>()</span>
  {
  }
}
</code></pre></div>
</details>
<h4 id="script-to-undo">Script to undo<a class="heading-anchor" href="#script-to-undo" aria-label="Copy link to “Script to undo”">#</a></h4>
<div class="code-block" data-lang="python"><div class="code-block__bar"><span class="code-block__lang">python</span></div><pre><code class="hljs language-python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">decrypt</span>(<span class="hljs-params">data: <span class="hljs-built_in">str</span></span>) -> <span class="hljs-built_in">str</span>:
    <span class="hljs-keyword">return</span> <span class="hljs-string">''</span>.join(<span class="hljs-built_in">chr</span>(<span class="hljs-built_in">ord</span>(c) ^ <span class="hljs-number">7</span>) <span class="hljs-keyword">for</span> c <span class="hljs-keyword">in</span> data)

encrypted_data = <span class="hljs-string">"P@I|`7kcXur2oXa4q4uz"</span>
decrypted_text = decrypt(encrypted_data)

<span class="hljs-built_in">print</span>(<span class="hljs-string">f"Decrypted text: <span class="hljs-subst">{decrypted_text}</span>"</span>)
</code></pre></div>
<h3 id="mile-high-flag-3">Mile High Flag 3<a class="heading-anchor" href="#mile-high-flag-3" aria-label="Copy link to “Mile High Flag 3”">#</a></h3>
<p>There is a mysterious signal message sent in MQTT</p>
<div class="code-block" data-lang="json"><div class="code-block__bar"><span class="code-block__lang">json</span></div><pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">"broadcast_event"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">12</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"data"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"tnNHCuwT/ixFcK9BIcyufJDohSTwxb76IoU+SJXdwO8SI63YopxMUcU2Vu5/JnpY"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"date"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"02/04/25"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"time"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"02:36AM UTC"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"message"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Mystery Signal"</span>
<span class="hljs-punctuation">}</span>
</code></pre></div>
<p>I am thinking that the badge must be able to decode the data. So to send my badge this data I edit the NVS data on my badge to point to my own MQTT server. (<a href="#editing-nvs-data">see Editing NVS Data in Extra</a>) Then I run the following command to dump most of memory.</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">python -m esptool dump_mem 0x40380000 393216 out.bin
</code></pre></div>
<p>We choose this memory section based on the ESP32 <a href="https://dl.espressif.com/public/esp32s3-mm.pdf">docs</a>. (S3 mem sections from @precurse) (After further reading of docs we miss some of the upper parts but as this command works I am not going to deal with the headaches and change it)</p>
<p>Running strings on the binary file yields no useful results so instead I run stringsSquared (<a href="#stringssquared">a tool I made for this, see Extra</a>)</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">python stringsSquared.py out.bin | grep WGN{
</code></pre></div>
<p>Which outputs the flag</p>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">Decode: WGN{h1gh_n00n_sh0wd0wn}
</code></pre></div>
<h3 id="mile-high-flag-4">Mile High Flag 4<a class="heading-anchor" href="#mile-high-flag-4" aria-label="Copy link to “Mile High Flag 4”">#</a></h3>
<p>The LED in the bottom right can randomly flash.</p>
<p><a href="https://youtube.com/shorts/ijG1FVzJa5s">https://youtube.com/shorts/ijG1FVzJa5s</a></p>
<p>The hardest part with this is seeing how many colors there are. It turns out there are only 4 (blue, red, green and cyan); the others are just the LED transitioning.</p>
<p>In our reading, the colors (red, blue, cyan) are “carriers” of the dot/dash information while green isn’t part of the signal at all – it is used as a separator. Moreover, when a letter is repeated three times (e.g. “RRR”, “CCC”, “BBB”) that indicates a long pulse (a “dash”), while a lone letter (e.g. “R”, “B”, “C”) is a short pulse (a “dot”). Finally, a single green “G” between pulses means “don’t break the letter” while a triple green “GGG” marks the end of a letter.</p>
<p>Let’s illustrate this with the data you provided. (We ignore the “///” header.)</p>
<p><strong>Step 1.</strong> Separate out pulses from the green separators</p>
<p>The non‐green lines (the pulses) are interleaved with green lines. The single “G” lines mark a pause between pulses within one Morse–encoded letter, while a “GGG” signals that one letter is finished. (That is why sometimes you see a “GGG” on its own.)</p>
<p>For example, the very first part is:</p>
<p><code>RRR G R G C G R GGG</code></p>
<p>Here the pulses are:</p>
<ul>
<li>RRR – a long red pulse (→ “dash”)</li>
<li>R – a short red pulse (→ “dot”)</li>
<li>C – a short cyan pulse (→ “dot”)</li>
<li>R – a short red pulse (→ “dot”)</li>
</ul>
<p>So the first Morse symbol is: dash dot dot dot</p>
<p><strong>Step 2.</strong> Interpret the dot/dash pulses as Morse code</p>
<p>Recall that in Morse code, dash dot dot dot is the letter B.</p>
<p>Now let’s briefly go through all the groups (each group being the pulses between “GGG” separators):</p>

























































































<div class="table-wrap" tabindex="0" role="region" aria-label="Table"><table><thead><tr><th>Group</th><th>Pulses</th><th>Morse</th><th>Letter</th></tr></thead><tbody><tr><td>1</td><td>RRR (dash), R (dot), C (dot), R (dot)</td><td>–···</td><td>B</td></tr><tr><td>2</td><td>B (dot), CCC (dash), B (dot), B (dot)</td><td>·–··</td><td>L</td></tr><tr><td>3</td><td>B (dot), RRR (dash)</td><td>·–</td><td>A</td></tr><tr><td>4</td><td>RRR (dash), BBB (dash), B (dot), C (dot)</td><td>––··</td><td>Z</td></tr><tr><td>5</td><td>B (dot)</td><td>·</td><td>E</td></tr><tr><td>6</td><td>CCC (dash)</td><td>–</td><td>T</td></tr><tr><td>7</td><td>B (dot), C (dot), C (dot), B (dot)</td><td>····</td><td>H</td></tr><tr><td>8</td><td>R (dot)</td><td>·</td><td>E</td></tr><tr><td>9</td><td>BBB (dash)</td><td>–</td><td>T</td></tr><tr><td>10</td><td>B (dot), CCC (dash), B (dot)</td><td>·–·</td><td>R</td></tr><tr><td>11</td><td>B (dot), BBB (dash)</td><td>·–</td><td>A</td></tr><tr><td>12</td><td>B (dot), R (dot)</td><td>··</td><td>I</td></tr><tr><td>13</td><td>B (dot), CCC (dash), R (dot), B (dot)</td><td>·–··</td><td>L</td></tr></tbody></table></div>
<p><strong>Step 3.</strong> Put it all together</p>
<p>Reading the letters in order gives:</p>
<p><code>B L A Z E T H E T R A I L</code></p>
<h3 id="mile-high-flag-5">Mile High Flag 5+<a class="heading-anchor" href="#mile-high-flag-5" aria-label="Copy link to “Mile High Flag 5+”">#</a></h3>
<p>The images on the site have some interesting metadata.</p>
<p><img src="/writeups/wwhf-2024-badge/mile-high-site-logo.webp" alt="Mile High badge site Stage Driver Orientation page with the logo image circled" decoding="async" loading="lazy" width="1600" height="567"></p>
<p><a href="https://www.aperisolve.com/d053eca26e2ceded99a82cdc14a707ed">https://www.aperisolve.com/d053eca26e2ceded99a82cdc14a707ed</a></p>
<p><img src="/writeups/wwhf-2024-badge/image-description-metadata.webp" alt="Image metadata Description field containing an encoded string" decoding="async" loading="lazy" width="1600" height="96"></p>
<p><img src="/writeups/wwhf-2024-badge/cyberchef-description-decode.webp" alt="CyberChef From Base64, From Base32 and ROT13 recipe decoding the description to URL,Paragraph,Word,Character Count" decoding="async" loading="lazy" width="1600" height="776"></p>
<p>This gives us how the messages will be organized.</p>
<p>Downloading all the images to a folder on my computer, I then run</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">strings * | awk <span class="hljs-string">'length($0) > 30'</span>
</code></pre></div>
<p><img src="/writeups/wwhf-2024-badge/strings-image-urls.webp" alt="strings output listing URLs with paragraph, word and character numbers hidden in the site images" decoding="async" loading="lazy" width="887" height="286"></p>
<p><img src="/writeups/wwhf-2024-badge/excel-left-formula.webp" alt="Excel LEFT formula pulling characters out of the referenced blog paragraph" decoding="async" loading="lazy" width="1600" height="448"></p>
<p>The output message is then the flag you put in.</p>
<h3 id="charger-fix">Charger Fix<a class="heading-anchor" href="#charger-fix" aria-label="Copy link to “Charger Fix”">#</a></h3>
<p>The TP4056 chip has a trace issue on the PCB. This can be solved by soldering a wire from pin 4 to 8. This just jumps the 5v to the chip enable pin telling the chip to work.</p>
<p>Image (do not roast the solder job, it was rushed at the conference with an iron that complains too much):</p>
<p><img src="/writeups/wwhf-2024-badge/charger-fix-jumper-wire.webp" alt="Mile High badge with a jumper wire soldered between pins 4 and 8 of the TP4056 charger chip" decoding="async" loading="lazy" width="1536" height="2048"></p>
<h3 id="other-random-things-about-this-badge">Other Random Things About This Badge<a class="heading-anchor" href="#other-random-things-about-this-badge" aria-label="Copy link to “Other Random Things About This Badge”">#</a></h3>
<p>The eyes are IR LEDs and don't seem to do anything interesting.</p>
<p>My flash dump: <a href="https://bucket.ics.red/flash_contents_noscreen.bin">https://bucket.ics.red/flash_contents_noscreen.bin</a></p>
<h2 id="further-reading">Further Reading<a class="heading-anchor" href="#further-reading" aria-label="Copy link to “Further Reading”">#</a></h2>
<h3 id="tcm-certification-affiliate-link">TCM Certification (Affiliate Link)<a class="heading-anchor" href="#tcm-certification-affiliate-link" aria-label="Copy link to “TCM Certification (Affiliate Link)”">#</a></h3>
<p><a href="https://certifications.tcm-sec.com/pipa/?ref=162">TCM Security IoT Hacking Course</a></p>
<p><a href="https://certifications.tcm-sec.com/pipa/?ref=162">https://certifications.tcm-sec.com/pipa/?ref=162</a></p>
<h3 id="matt-brown-youtube">Matt Brown YouTube<a class="heading-anchor" href="#matt-brown-youtube" aria-label="Copy link to “Matt Brown YouTube”">#</a></h3>
<p>Channel: <a href="https://www.youtube.com/@mattbrwn">https://www.youtube.com/@mattbrwn</a></p>
<p>Discord: <a href="https://discord.com/invite/GjVxUnrQKC">https://discord.com/invite/GjVxUnrQKC</a></p>
<h3 id="antisyphon-iot-hacking-class">Antisyphon IoT Hacking Class<a class="heading-anchor" href="#antisyphon-iot-hacking-class" aria-label="Copy link to “Antisyphon IoT Hacking Class”">#</a></h3>
<p><a href="https://www.antisyphontraining.com/course/intro-to-iot-hacking-with-rick-wisser-and-dave-fletcher/">https://www.antisyphontraining.com/course/intro-to-iot-hacking-with-rick-wisser-and-dave-fletcher/</a></p>
<h2 id="custom-firmware">Custom Firmware<a class="heading-anchor" href="#custom-firmware" aria-label="Copy link to “Custom Firmware”">#</a></h2>
<p><a href="https://github.com/Cooperw/ww-pirate-badge-fw">https://github.com/Cooperw/ww-pirate-badge-fw</a></p>
<p>This can be used to add custom LED flash sequences to your badge.</p>
<p><img src="/writeups/wwhf-2024-badge/custom-firmware-leds.webp" alt="Mile High badge running custom firmware with its LEDs lit" decoding="async" loading="lazy" width="1152" height="2048"></p>
<h3 id="adding-a-reset">Adding a Reset<a class="heading-anchor" href="#adding-a-reset" aria-label="Copy link to “Adding a Reset”">#</a></h3>
<p>If you can talk to the board you might need to hit the reset "button". You need to bridge these two pins (top and bottom). I just soldered a wire.</p>
<p><img src="/writeups/wwhf-2024-badge/reset-pins-wire.webp" alt="WWHF badge with a wire soldered across the reset pins" decoding="async" loading="lazy" width="1600" height="1200"></p>
<h2 id="extra">Extra<a class="heading-anchor" href="#extra" aria-label="Copy link to “Extra”">#</a></h2>
<h3 id="how-the-scanners-report-a-scan">How the scanners report a scan<a class="heading-anchor" href="#how-the-scanners-report-a-scan" aria-label="Copy link to “How the scanners report a scan”">#</a></h3>
<p>You might notice some activity on MQTT when you scan your badge but this is not how the server determines if a scan takes place. It uses an HTTP POST request to <code>/bounties</code>.</p>
<p>It can be represented as</p>
<div class="code-block" data-lang="python"><div class="code-block__bar"><span class="code-block__lang">python</span></div><pre><code class="hljs language-python"><span class="hljs-keyword">import</span> requests

url = <span class="hljs-string">"https://hardwarelabs.io/bounties"</span>

payload = <span class="hljs-string">"{\r\n    \"mac_address\": \"30:30:F9:7B:90:AC\",\r\n    \"rfid\": \"f4bfd940\"\r\n}"</span>
headers = {}

response = requests.request(<span class="hljs-string">"POST"</span>, url, headers=headers, data=payload)

<span class="hljs-built_in">print</span>(response.text)
</code></pre></div>
<p>or</p>
<p><img src="/writeups/wwhf-2024-badge/postman-bounty-request.webp" alt="Postman POST request to hardwarelabs.io/bounties with a mac_address and rfid JSON body" decoding="async" loading="lazy" width="651" height="252"></p>
<p>where <code>rfid</code> is the lowercase UID. We discovered the format through the serial messages produced by the badge reader.</p>
<p>Badge reader serial:</p>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">[BADGE] Loading badge config...
[BADGE] Loading bounties...
[BADGE] WWHF Badge v1.27
[BADGE] EC:DA:3B:5E:12:78
[WiFi] Wi-Fi ready
[WiFi] Station mode started
[BOUNTY] Mounted bounty cache filesystem
[WiFi] Station connected to AP
[RFID] Bounty Captured: f4bfd940
</code></pre></div>
<p>From here I wrote a script to brute force all the OUIs we saw on the network to try to hunt down the last event 5 scanner (We later found out this doesn't exist)</p>
<h4 id="scanner-brute-force">Scanner Brute Force<a class="heading-anchor" href="#scanner-brute-force" aria-label="Copy link to “Scanner Brute Force”">#</a></h4>
<div class="code-block code-block--collapsible" data-lang="python"><input type="checkbox" id="code-more-3" class="code-block__toggle"><div class="code-block__bar"><span class="code-block__lang">python</span></div><pre><code class="hljs language-python"><span class="hljs-keyword">import</span> aiohttp
<span class="hljs-keyword">import</span> asyncio
<span class="hljs-keyword">import</span> json
<span class="hljs-keyword">import</span> itertools
<span class="hljs-keyword">from</span> tqdm <span class="hljs-keyword">import</span> tqdm

url = <span class="hljs-string">"https://hardwarelabs.io/bounties"</span>

<span class="hljs-comment"># OUI of the scanners</span>
ouis = [<span class="hljs-string">"34:B7:DA:5E"</span>,<span class="hljs-string">"34:B7:DA:5F"</span>,<span class="hljs-string">"34:B7:DA:5D"</span>]

mac_suffix_range = <span class="hljs-built_in">range</span>(<span class="hljs-number">0x00</span>, <span class="hljs-number">0xFF</span> + <span class="hljs-number">1</span>)  <span class="hljs-comment"># This will range from 00 to FF (0-255 in hex)</span>

headers = {
    <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
    <span class="hljs-string">'Authorization'</span>: <span class="hljs-string">'Bearer &#x3C;your-api-token>'</span>
}
rfid = <span class="hljs-string">"f4bfd940"</span>

<span class="hljs-comment"># Limit the number of concurrent requests</span>
MAX_CONCURRENT_REQUESTS = <span class="hljs-number">1000</span>

<span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">send_request</span>(<span class="hljs-params">session, mac_address</span>):
    payload = json.dumps({
        <span class="hljs-string">"mac_address"</span>: mac_address,
        <span class="hljs-string">"rfid"</span>: rfid
    })

    <span class="hljs-keyword">async</span> <span class="hljs-keyword">with</span> session.post(url, headers=headers, data=payload) <span class="hljs-keyword">as</span> response:
       <span class="hljs-comment"># print(await response.text())</span>
        <span class="hljs-comment">#print(response.status)</span>
        <span class="hljs-keyword">if</span> response.status == <span class="hljs-number">201</span>:  <span class="hljs-comment"># Only print if the status is 201</span>
            response_text = <span class="hljs-keyword">await</span> response.text()
            <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Testing MAC: <span class="hljs-subst">{mac_address}</span> -> Response: <span class="hljs-subst">{response_text}</span>"</span>)
            <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>  <span class="hljs-comment"># Indicating success</span>
        <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>  <span class="hljs-comment"># Indicating failure</span>

<span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">brute_force_mac_addresses</span>():
    total_mac_addresses = <span class="hljs-built_in">len</span>(ouis) * (<span class="hljs-number">0xFF</span> + <span class="hljs-number">1</span>) * (<span class="hljs-number">0xFF</span> + <span class="hljs-number">1</span>)
    progress = tqdm(total=total_mac_addresses, desc=<span class="hljs-string">"Brute-forcing MAC addresses"</span>, unit=<span class="hljs-string">"request"</span>)

    <span class="hljs-comment"># Set up the aiohttp session</span>
    <span class="hljs-keyword">async</span> <span class="hljs-keyword">with</span> aiohttp.ClientSession() <span class="hljs-keyword">as</span> session:
        tasks = []
        <span class="hljs-keyword">for</span> oui <span class="hljs-keyword">in</span> ouis:
            <span class="hljs-keyword">for</span> suffix1, suffix2 <span class="hljs-keyword">in</span> itertools.product(mac_suffix_range, repeat=<span class="hljs-number">2</span>):
                mac_address = <span class="hljs-string">f"<span class="hljs-subst">{oui}</span>:<span class="hljs-subst">{suffix1:02X}</span>:<span class="hljs-subst">{suffix2:02X}</span>"</span>
                <span class="hljs-built_in">print</span>(mac_address)

                <span class="hljs-comment"># Create the task for each request</span>
                task = asyncio.ensure_future(send_request(session, mac_address))
                tasks.append(task)

                <span class="hljs-comment"># Send a batch of requests</span>
                <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(tasks) >= MAX_CONCURRENT_REQUESTS:
                    results = <span class="hljs-keyword">await</span> asyncio.gather(*tasks)
                    tasks = []  <span class="hljs-comment"># Reset the task list</span>
                    progress.update(MAX_CONCURRENT_REQUESTS)

        <span class="hljs-comment"># Await any remaining tasks</span>
        <span class="hljs-keyword">if</span> tasks:
            <span class="hljs-keyword">await</span> asyncio.gather(*tasks)
            progress.update(<span class="hljs-built_in">len</span>(tasks))

    progress.close()  <span class="hljs-comment"># Close the progress bar when done</span>

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    asyncio.run(brute_force_mac_addresses())
</code></pre><label for="code-more-3" class="code-block__more"><span class="code-block__more-closed">Show all 68 lines</span><span class="code-block__more-open">Show less</span></label></div>
<h4 id="all-scans-solver">All scans solver<a class="heading-anchor" href="#all-scans-solver" aria-label="Copy link to “All scans solver”">#</a></h4>
<p><strong>This code is from @CW0 and his <a href="https://github.com/Cooperw/ctf/tree/master/2024-10-11-wwhf24">repo</a></strong></p>
<div class="code-block code-block--collapsible" data-lang="python"><input type="checkbox" id="code-more-4" class="code-block__toggle"><div class="code-block__bar"><span class="code-block__lang">python</span></div><pre><code class="hljs language-python"><span class="hljs-comment"># claim_bounties.py :: GPT-4o</span>

<span class="hljs-comment"># usage: python claim_bounties.py 7bdfd941</span>

<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> sys
<span class="hljs-keyword">import</span> argparse
<span class="hljs-keyword">import</span> time
<span class="hljs-keyword">import</span> os

<span class="hljs-string">'''
export API_BEARER_TOKEN="&#x3C;your-api-token>"

7bdfd940 shiloh
f4b4d940 logix
7159d940 vort
fcb9d940 alex
a3c9d940 rsulliva
57b4d940 netwarsninja66
'''</span>

<span class="hljs-keyword">def</span> <span class="hljs-title function_">main</span>():
    <span class="hljs-comment"># Define the list of MAC addresses</span>
    mac_addresses = [
        <span class="hljs-string">"EC:DA:3B:5D:D0:7C"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:0C:24"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:0C:34"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:0C:88"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:11:D8"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:11:E8"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:11:F0"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:11:F4"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:12:30"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:12:34"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:12:78"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:12:8C"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:12:90"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:12:A8"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:16:D0"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:16:F0"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:1F:80"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:1F:C8"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:48:30"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:53:F8"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:6D:48"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:6D:54"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:6D:5C"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:6D:68"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:6D:70"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:6D:80"</span>,
        <span class="hljs-string">"EC:DA:3B:5F:5B:50"</span>,
        <span class="hljs-string">"EC:DA:3B:5F:FD:90"</span>,
        <span class="hljs-string">"EC:DA:3B:5F:FD:94"</span>,
        <span class="hljs-string">"EC:DA:3B:5F:FD:AC"</span>,
        <span class="hljs-string">"EC:DA:3B:5F:FD:B4"</span>,
        <span class="hljs-string">"EC:DA:3B:5F:FD:C0"</span>,
        <span class="hljs-string">"EC:DA:3B:5F:FD:C4"</span>,
        <span class="hljs-string">"EC:DA:3B:5E:17:30"</span> <span class="hljs-comment"># unlisted</span>
    ]

    <span class="hljs-comment"># Set up command-line argument parsing</span>
    parser = argparse.ArgumentParser(description=<span class="hljs-string">'Send POST requests to hardwarelabs.io/bounties.'</span>)
    parser.add_argument(<span class="hljs-string">'rfid'</span>, <span class="hljs-built_in">type</span>=<span class="hljs-built_in">str</span>, <span class="hljs-built_in">help</span>=<span class="hljs-string">'RFID value to include in the POST request.'</span>)
    args = parser.parse_args()

    rfid = args.rfid

    endpoint = <span class="hljs-string">"https://hardwarelabs.io/bounties"</span>

    <span class="hljs-comment"># Retrieve the Bearer Token from environment variables</span>
    bearer_token = os.getenv(<span class="hljs-string">'API_BEARER_TOKEN'</span>)
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> bearer_token:
        <span class="hljs-built_in">print</span>(<span class="hljs-string">"Error: API_BEARER_TOKEN environment variable not set."</span>)
        sys.exit(<span class="hljs-number">1</span>)

    headers = {
        <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
        <span class="hljs-string">"Authorization"</span>: <span class="hljs-string">f"Bearer <span class="hljs-subst">{bearer_token}</span>"</span>
    }

    success_count = <span class="hljs-number">0</span>
    error_count = <span class="hljs-number">0</span>

    <span class="hljs-keyword">for</span> index, mac <span class="hljs-keyword">in</span> <span class="hljs-built_in">enumerate</span>(mac_addresses, start=<span class="hljs-number">1</span>):
        payload = {
            <span class="hljs-string">"mac_address"</span>: mac,
            <span class="hljs-string">"rfid"</span>: rfid
        }

        <span class="hljs-keyword">try</span>:
            response = requests.post(endpoint, json=payload, headers=headers)
            <span class="hljs-keyword">if</span> response.status_code == <span class="hljs-number">201</span>:
                success_count += <span class="hljs-number">1</span>
                <span class="hljs-built_in">print</span>(<span class="hljs-string">f"SUCCESS [<span class="hljs-subst">{index}</span>/<span class="hljs-subst">{<span class="hljs-built_in">len</span>(mac_addresses)}</span>]: MAC <span class="hljs-subst">{mac}</span> - Status Code: <span class="hljs-subst">{response.status_code}</span>"</span>)
            <span class="hljs-keyword">else</span>:
                error_count += <span class="hljs-number">1</span>
                <span class="hljs-built_in">print</span>(<span class="hljs-string">f"ERROR   [<span class="hljs-subst">{index}</span>/<span class="hljs-subst">{<span class="hljs-built_in">len</span>(mac_addresses)}</span>]: MAC <span class="hljs-subst">{mac}</span> - Status Code: <span class="hljs-subst">{response.status_code}</span> - Response: <span class="hljs-subst">{response.text}</span>"</span>)
        <span class="hljs-keyword">except</span> requests.exceptions.RequestException <span class="hljs-keyword">as</span> e:
            error_count += <span class="hljs-number">1</span>
            <span class="hljs-built_in">print</span>(<span class="hljs-string">f"EXCEPTION[<span class="hljs-subst">{index}</span>/<span class="hljs-subst">{<span class="hljs-built_in">len</span>(mac_addresses)}</span>]: MAC <span class="hljs-subst">{mac}</span> - <span class="hljs-subst">{e}</span>"</span>)

        <span class="hljs-comment"># Rate limiting: Wait for 5 seconds before the next request, except after the last request</span>
        <span class="hljs-keyword">if</span> index &#x3C; <span class="hljs-built_in">len</span>(mac_addresses):
            time.sleep(<span class="hljs-number">5</span>)  <span class="hljs-comment"># Pause for 5 seconds</span>

    <span class="hljs-built_in">print</span>(<span class="hljs-string">"\n=== Summary ==="</span>)
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Total Requests: <span class="hljs-subst">{<span class="hljs-built_in">len</span>(mac_addresses)}</span>"</span>)
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Successful (201): <span class="hljs-subst">{success_count}</span>"</span>)
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Failed: <span class="hljs-subst">{error_count}</span>"</span>)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    main()
</code></pre><label for="code-more-4" class="code-block__more"><span class="code-block__more-closed">Show all 112 lines</span><span class="code-block__more-open">Show less</span></label></div>
<h3 id="badge-reader-firmware">Badge Reader Firmware<a class="heading-anchor" href="#badge-reader-firmware" aria-label="Copy link to “Badge Reader Firmware”">#</a></h3>
<p>You can download it from <a href="https://bucket.ics.red/reader.bin">here</a>: <a href="https://bucket.ics.red/reader.bin">https://bucket.ics.red/reader.bin</a></p>
<h3 id="simulating-the-binary">Simulating the Binary<a class="heading-anchor" href="#simulating-the-binary" aria-label="Copy link to “Simulating the Binary”">#</a></h3>
<p>Cutter will attempt to simulate the code.</p>
<p><img src="/writeups/wwhf-2024-badge/cutter-start-emulation.webp" alt="Cutter debug menu with Start emulation selected" decoding="async" loading="lazy" width="275" height="139"></p>
<h3 id="extra-mqtt-topic">Extra MQTT Topic<a class="heading-anchor" href="#extra-mqtt-topic" aria-label="Copy link to “Extra MQTT Topic”">#</a></h3>
<p>Badge registration is performed using the MQTT topic <code>/badges/{BadgeName}</code></p>
<h3 id="stringssquared">StringsSquared<a class="heading-anchor" href="#stringssquared" aria-label="Copy link to “StringsSquared”">#</a></h3>
<p>Decodes hex strings from memory</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">python stringsSquared.py mem_dump.bin
</code></pre></div>
<details>
<summary>Code</summary>
<div class="code-block" data-lang="python"><div class="code-block__bar"><span class="code-block__lang">python</span></div><pre><code class="hljs language-python"><span class="hljs-keyword">import</span> subprocess
<span class="hljs-keyword">import</span> argparse

<span class="hljs-keyword">def</span> <span class="hljs-title function_">strings_and_hex_decode</span>(<span class="hljs-params">file_path</span>):
    <span class="hljs-keyword">try</span>:
        <span class="hljs-comment"># Step 1: Run `strings` on the file and capture the output</span>
        result = subprocess.run([<span class="hljs-string">'strings'</span>, file_path], stdout=subprocess.PIPE, text=<span class="hljs-literal">True</span>)
        output = result.stdout

        <span class="hljs-comment"># Step 2: Process each line of the output</span>
        <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> output.splitlines():
            <span class="hljs-comment"># Remove whitespace and check if it's a valid hex string</span>
            clean_line = line.strip()
            <span class="hljs-keyword">try</span>:
                <span class="hljs-comment"># Attempt to decode as hex</span>
                decoded_line = <span class="hljs-built_in">bytes</span>.fromhex(clean_line).decode(<span class="hljs-string">'utf-8'</span>)
                <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Original: <span class="hljs-subst">{clean_line}</span> | Decoded: <span class="hljs-subst">{decoded_line}</span>"</span>)
            <span class="hljs-keyword">except</span> ValueError:
                <span class="hljs-comment"># Skip lines that are not valid hex</span>
                <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Not hex: <span class="hljs-subst">{clean_line}</span>"</span>)
    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
        <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Error: <span class="hljs-subst">{e}</span>"</span>)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    <span class="hljs-comment"># Argument parsing</span>
    parser = argparse.ArgumentParser(description=<span class="hljs-string">"Extract strings from a file and decode hex lines."</span>)
    parser.add_argument(<span class="hljs-string">"file"</span>, <span class="hljs-built_in">help</span>=<span class="hljs-string">"Path to the file to process"</span>)
    args = parser.parse_args()

    <span class="hljs-comment"># Call the function with the provided file path</span>
    strings_and_hex_decode(args.file)
</code></pre></div>
</details>
<h3 id="editing-nvs-data">Editing NVS Data<a class="heading-anchor" href="#editing-nvs-data" aria-label="Copy link to “Editing NVS Data”">#</a></h3>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">git <span class="hljs-built_in">clone</span> https://github.com/ladderlogix/esp_nvs_editor
</code></pre></div>
<p>Get <code>nvs.bin</code> from ESP32 Image Parser when dumping the partition.</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">python main.py json nvs.bin nvs.json
</code></pre></div>
<p>Open the JSON. Strings are hex encoded (will change in a future PR).</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">python main.py bin nvs.json nvs.patched
</code></pre></div>
<p>Flash to the chip (run from ESP-IDF 5.3, under <code>components\partition_table</code>):</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">python .\parttool.py --port COM5 write_partition --partition-name=nvs --input <span class="hljs-string">"D:\NVSReplacing\nvs.patched.bin"</span>
</code></pre></div>
<h3 id="binaryshiftdecode">binaryShiftDecode<a class="heading-anchor" href="#binaryshiftdecode" aria-label="Copy link to “binaryShiftDecode”">#</a></h3>
<p>Tool to help read binary but if the data order is moved around. This was a tool I made in preparation for Mile High.</p>
<p>Takes in a file with binary data on each line, i.e.</p>
<div class="code-block"><div class="code-block__bar"><span class="code-block__lang"></span></div><pre><code class="language-text">0011
1011
0110
</code></pre></div>
<p>and will try to hex decode it.</p>
<div class="code-block" data-lang="bash"><div class="code-block__bar"><span class="code-block__lang">bash</span></div><pre><code class="hljs language-bash">python main.py
</code></pre></div>
<p>A GUI will pop up where you can pick a file.</p>
<details>
<summary>Code</summary>
<div class="code-block code-block--collapsible" data-lang="python"><input type="checkbox" id="code-more-5" class="code-block__toggle"><div class="code-block__bar"><span class="code-block__lang">python</span></div><pre><code class="hljs language-python"><span class="hljs-keyword">import</span> tkinter <span class="hljs-keyword">as</span> tk
<span class="hljs-keyword">from</span> tkinter <span class="hljs-keyword">import</span> filedialog, messagebox

<span class="hljs-comment"># Shift a binary string to the right</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">shift_binary</span>(<span class="hljs-params">binary</span>):
    <span class="hljs-keyword">return</span> binary[-<span class="hljs-number">1</span>] + binary[:-<span class="hljs-number">1</span>]

<span class="hljs-comment"># Decode binary string to ASCII character</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">decode_binary</span>(<span class="hljs-params">binary</span>):
    <span class="hljs-keyword">try</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">chr</span>(<span class="hljs-built_in">int</span>(binary, <span class="hljs-number">2</span>))
    <span class="hljs-keyword">except</span> ValueError:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"?"</span>

<span class="hljs-comment"># Load binary data from a file</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">load_binary_data</span>():
    <span class="hljs-string">"""Prompt the user to select a file and load binary data."""</span>
    <span class="hljs-keyword">global</span> binary_data, current_data

    <span class="hljs-comment"># Open file dialog to select a file</span>
    file_path = filedialog.askopenfilename(
        title=<span class="hljs-string">"Select a file"</span>,
        filetypes=((<span class="hljs-string">"Text Files"</span>, <span class="hljs-string">"*.txt"</span>), (<span class="hljs-string">"All Files"</span>, <span class="hljs-string">"*.*"</span>))
    )

    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> file_path:
        <span class="hljs-keyword">return</span>  <span class="hljs-comment"># User canceled the file selection</span>

    <span class="hljs-keyword">try</span>:
        <span class="hljs-keyword">with</span> <span class="hljs-built_in">open</span>(file_path, <span class="hljs-string">"r"</span>) <span class="hljs-keyword">as</span> file:
            binary_data = [line.strip() <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> file.readlines() <span class="hljs-keyword">if</span> line.strip()]

        <span class="hljs-comment"># Validate binary strings</span>
        <span class="hljs-keyword">for</span> binary <span class="hljs-keyword">in</span> binary_data:
            <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(binary) != <span class="hljs-number">8</span> <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> <span class="hljs-built_in">set</span>(binary).issubset({<span class="hljs-string">'0'</span>, <span class="hljs-string">'1'</span>}):
                <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Invalid binary string: <span class="hljs-subst">{binary}</span>"</span>)

        <span class="hljs-comment"># Set initial data and update the display</span>
        current_data = binary_data[:]
        update_display()
    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
        messagebox.showerror(<span class="hljs-string">"Error"</span>, <span class="hljs-string">f"Failed to load data: <span class="hljs-subst">{e}</span>"</span>)
        binary_data = []
        current_data = []

<span class="hljs-comment"># Update the display</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">update_display</span>():
    <span class="hljs-string">"""Update the binary strings and their decoded characters displayed in the GUI."""</span>
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-built_in">len</span>(binary_labels)):
        <span class="hljs-keyword">if</span> i &#x3C; <span class="hljs-built_in">len</span>(current_data):
            binary = current_data[i]
            binary_labels[i].config(text=binary)
            decoded_labels[i].config(text=decode_binary(binary))
        <span class="hljs-keyword">else</span>:
            binary_labels[i].config(text=<span class="hljs-string">""</span>)  <span class="hljs-comment"># Clear unused labels</span>
            decoded_labels[i].config(text=<span class="hljs-string">""</span>)

<span class="hljs-comment"># Shift binary strings to the right and update the display</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">next_page</span>():
    <span class="hljs-string">"""Shift all binary strings to the right."""</span>
    <span class="hljs-keyword">global</span> current_data
    current_data = [shift_binary(binary) <span class="hljs-keyword">for</span> binary <span class="hljs-keyword">in</span> current_data]
    update_display()

<span class="hljs-comment"># Initialize the GUI</span>
root = tk.Tk()
root.title(<span class="hljs-string">"Binary Viewer with Shifts and Decoding"</span>)

<span class="hljs-comment"># Initialize variables</span>
binary_data = []
current_data = []

<span class="hljs-comment"># Frame for binary strings and decoded characters</span>
frame = tk.Frame(root)
frame.pack(pady=<span class="hljs-number">10</span>)

<span class="hljs-comment"># Create labels for binary strings and decoded characters</span>
binary_labels = []
decoded_labels = []
rows_to_display = <span class="hljs-number">10</span>  <span class="hljs-comment"># Adjust based on how many rows you want visible at once</span>

<span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(rows_to_display):
    row = tk.Frame(frame)
    row.pack(side=tk.TOP, fill=tk.X, pady=<span class="hljs-number">2</span>)

    binary_label = tk.Label(row, width=<span class="hljs-number">15</span>, text=<span class="hljs-string">""</span>, anchor=<span class="hljs-string">"w"</span>)
    binary_label.pack(side=tk.LEFT, padx=<span class="hljs-number">5</span>)
    binary_labels.append(binary_label)

    decoded_label = tk.Label(row, width=<span class="hljs-number">5</span>, text=<span class="hljs-string">""</span>, anchor=<span class="hljs-string">"w"</span>)
    decoded_label.pack(side=tk.LEFT, padx=<span class="hljs-number">5</span>)
    decoded_labels.append(decoded_label)

<span class="hljs-comment"># Buttons for file loading and shifting</span>
button_frame = tk.Frame(root)
button_frame.pack(pady=<span class="hljs-number">5</span>)

load_button = tk.Button(button_frame, text=<span class="hljs-string">"Load File"</span>, command=load_binary_data)
load_button.pack(side=tk.LEFT, padx=<span class="hljs-number">5</span>)

next_button = tk.Button(button_frame, text=<span class="hljs-string">"Next"</span>, command=next_page)
next_button.pack(side=tk.LEFT, padx=<span class="hljs-number">5</span>)

<span class="hljs-comment"># Start the GUI event loop</span>
root.mainloop()
</code></pre><label for="code-more-5" class="code-block__more"><span class="code-block__more-closed">Show all 105 lines</span><span class="code-block__more-open">Show less</span></label></div>
</details>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[CyberForce 2024 Adventure Writeup]]></title>
            <link>https://ics.red/posts/cyberforce-adventure-2024</link>
            <guid isPermaLink="false">https://ics.red/posts/cyberforce-adventure-2024</guid>
            <pubDate>Tue, 18 Jun 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Solutions to Department of Energy CyberForce Adventure 2024 anomalies: browser-history forensics, WAF log analysis, steganography, C++ reversing, phishing triage, and trivia.]]></description>
            <content:encoded><![CDATA[<p>These are my solutions to the Department of Energy's CyberForce Adventure 2024 anomalies, grouped by the competition's categories. For the multiple-choice trivia, the correct answer is in bold.</p>
<h2 id="securely-provision">Securely Provision<a class="heading-anchor" href="#securely-provision" aria-label="Copy link to “Securely Provision”">#</a></h2>
<h3 id="anomaly-74-76-tears-of-the-analyst-c-class-reversing">Anomaly 74-76: Tears of the Analyst-C++ Class Reversing<a class="heading-anchor" href="#anomaly-74-76-tears-of-the-analyst-c-class-reversing" aria-label="Copy link to “Anomaly 74-76: Tears of the Analyst-C++ Class Reversing”">#</a></h3>
<h4 id="anomaly-74">Anomaly 74<a class="heading-anchor" href="#anomaly-74" aria-label="Copy link to “Anomaly 74”">#</a></h4>
<p><img src="/writeups/cyberforce-adventure-2024/gerudo-constructor.webp" alt="Decompiled Gerudo::Gerudo constructor calling Citizen::Citizen with a string and two ints" decoding="async" loading="lazy" width="895" height="395"></p>
<p>We can see that we are inheriting stuff from Citizen</p>
<h4 id="anomaly-75">Anomaly 75<a class="heading-anchor" href="#anomaly-75" aria-label="Copy link to “Anomaly 75”">#</a></h4>
<p><img src="/writeups/cyberforce-adventure-2024/hylian-print-info.webp" alt="Decompiled Hylian::print_info function" decoding="async" loading="lazy" width="713" height="155"></p>
<p>Looking at all the functions we see that this function is inherited</p>
<h4 id="anomaly-76">Anomaly 76<a class="heading-anchor" href="#anomaly-76" aria-label="Copy link to “Anomaly 76”">#</a></h4>
<p><img src="/writeups/cyberforce-adventure-2024/gerudo-constructor.webp" alt="Decompiled Gerudo::Gerudo constructor calling Citizen::Citizen with a string and two ints" decoding="async" loading="lazy" width="895" height="395"></p>
<p>We can see we are passing a string and two ints into the Citizen class</p>
<h2 id="protect-and-defend">Protect and Defend<a class="heading-anchor" href="#protect-and-defend" aria-label="Copy link to “Protect and Defend”">#</a></h2>
<h3 id="anomaly-21-25-tps-report">Anomaly 21-25: TPS Report<a class="heading-anchor" href="#anomaly-21-25-tps-report" aria-label="Copy link to “Anomaly 21-25: TPS Report”">#</a></h3>
<h4 id="anomaly-21">Anomaly 21<a class="heading-anchor" href="#anomaly-21" aria-label="Copy link to “Anomaly 21”">#</a></h4>
<p>Since we were not able to access it for a very long time and that the server just stored data we can mark it 6 <code>Denial of Non-Critical Services</code></p>
<h4 id="anomaly-22">Anomaly 22<a class="heading-anchor" href="#anomaly-22" aria-label="Copy link to “Anomaly 22”">#</a></h4>
<p>Since no data was taken we give it 1 <code>No Impact</code></p>
<h4 id="anomaly-23">Anomaly 23<a class="heading-anchor" href="#anomaly-23" aria-label="Copy link to “Anomaly 23”">#</a></h4>
<p>Since this was a public server it falls into cat 2 <code>Business Network</code></p>
<h4 id="anomaly-24">Anomaly 24<a class="heading-anchor" href="#anomaly-24" aria-label="Copy link to “Anomaly 24”">#</a></h4>
<p>Since they were not able to access it then <code>Availability was targeted</code></p>
<h4 id="anomaly-25">Anomaly 25<a class="heading-anchor" href="#anomaly-25" aria-label="Copy link to “Anomaly 25”">#</a></h4>
<p>We got to do some math and subtracting them <code>1715134320 - 1715128980 / 60</code> gives us 89</p>
<h3 id="anomaly-26-28-nice-day-for-phishing">Anomaly 26-28: Nice Day for Phishing<a class="heading-anchor" href="#anomaly-26-28-nice-day-for-phishing" aria-label="Copy link to “Anomaly 26-28: Nice Day for Phishing”">#</a></h3>
<h4 id="ticket-1">Ticket 1<a class="heading-anchor" href="#ticket-1" aria-label="Copy link to “Ticket 1”">#</a></h4>
<p><code>Spam</code>, does not seem to try to impersonate, clone, etc.</p>
<h4 id="ticket-2">Ticket 2<a class="heading-anchor" href="#ticket-2" aria-label="Copy link to “Ticket 2”">#</a></h4>
<p>First the from email stands out as very odd. But since it is targeting a high value individual it is whaling.</p>
<h4 id="ticket-3">Ticket 3<a class="heading-anchor" href="#ticket-3" aria-label="Copy link to “Ticket 3”">#</a></h4>
<p>Since this is a text, this is smishing.</p>
<h3 id="anomaly-30-trivia-02">Anomaly 30: Trivia 02<a class="heading-anchor" href="#anomaly-30-trivia-02" aria-label="Copy link to “Anomaly 30: Trivia 02”">#</a></h3>
<p>What is the primary purpose of a bind shell payload in a penetration testing scenario?</p>
<ul>
<li>a) To establish a reverse connection from the attacker's machine to the target system. - That is a reverse shell</li>
<li>b) To execute arbitrary commands on the target system and return the results to the attacker. - Not the best answer</li>
<li><strong>c) To open a network port on the target system and listen for incoming connections from the attacker.</strong></li>
<li>d) To encrypt communication between the attacker and the target system to evade detection. - Not its goal</li>
</ul>
<h3 id="anomaly-31-trivia-03">Anomaly 31: Trivia 03<a class="heading-anchor" href="#anomaly-31-trivia-03" aria-label="Copy link to “Anomaly 31: Trivia 03”">#</a></h3>
<p>Explain the difference between symmetric and asymmetric encryption</p>
<ul>
<li>a) Symmetric encryption uses two different keys for encryption and decryption, while asymmetric encryption uses the same key for both processes.</li>
<li>b) Symmetric encryption is faster than asymmetric encryption, while asymmetric encryption offers better security.</li>
<li>c) Symmetric encryption uses a pair of keys: a public key for encryption and a private key for decryption, while asymmetric encryption uses only one key for both processes.</li>
<li><strong>d) Symmetric encryption uses a single key for both encryption and decryption, while asymmetric encryption uses a pair of keys: a public key for encryption and a private key for decryption.</strong></li>
</ul>
<h3 id="anomaly-32-trivia-04">Anomaly 32: Trivia 04<a class="heading-anchor" href="#anomaly-32-trivia-04" aria-label="Copy link to “Anomaly 32: Trivia 04”">#</a></h3>
<p>Which of the following is a technique often utilized by attackers to evade detection by security tools and defenders, involving the encryption of malicious payloads to bypass signature-based detection mechanisms?</p>
<ul>
<li>a) Cross-site scripting (XSS) - This is an attack not an evasion mechanism</li>
<li>b) DNS tunneling - This is a method of exfiltration but can be viewed and stopped</li>
<li>c) Data exfiltration - This is just a term and not a method</li>
<li><strong>d) Payload obfuscation</strong></li>
</ul>
<h3 id="anomaly-33-trivia-05">Anomaly 33: Trivia 05<a class="heading-anchor" href="#anomaly-33-trivia-05" aria-label="Copy link to “Anomaly 33: Trivia 05”">#</a></h3>
<p>In a penetration testing scenario, which of the following methods is commonly used to escalate privileges on a compromised system when traditional methods fail?</p>
<ul>
<li>a) Reverse engineering - Used to analyse malware</li>
<li>b) Fuzzing - This is most commonly used to find flaws in a system</li>
<li><strong>c) Kernel exploitation</strong></li>
<li>d) DLL injection - While possible not that common</li>
</ul>
<h3 id="anomaly-34-trivia-06">Anomaly 34: Trivia 06<a class="heading-anchor" href="#anomaly-34-trivia-06" aria-label="Copy link to “Anomaly 34: Trivia 06”">#</a></h3>
<p>Which of the following is a technique used in a buffer overflow attack to redirect the program's execution flow to malicious code injected by the attacker?</p>
<ul>
<li>a) Code obfuscation - Not a bin attack</li>
<li>b) Integer overflow - This just makes a number negative or positive, not touching control flow</li>
<li><strong>c) Return-oriented programming (ROP)</strong> - We are trying to overwrite the return address</li>
<li>d) Cross-site scripting (XSS) - Not a bin attack</li>
</ul>
<h3 id="anomaly-35-trivia-07">Anomaly 35: Trivia 07<a class="heading-anchor" href="#anomaly-35-trivia-07" aria-label="Copy link to “Anomaly 35: Trivia 07”">#</a></h3>
<p>During a penetration test, the tester discovers an outdated FTP server running on the target network. Which of the following techniques would be MOST effective for exploiting this vulnerability?</p>
<ul>
<li><strong>a) Directory traversal attack</strong> - Deals with paths</li>
<li>b) Cross-site scripting (XSS) - Most common on websites</li>
<li>c) SQL injection - Common on databases</li>
<li>d) Buffer overflow exploit - Good if we have a function we are trying to access</li>
</ul>
<h3 id="anomaly-36-trivia-08">Anomaly 36: Trivia 08<a class="heading-anchor" href="#anomaly-36-trivia-08" aria-label="Copy link to “Anomaly 36: Trivia 08”">#</a></h3>
<p>In a penetration testing scenario, what is the primary purpose of using a reverse shell payload?</p>
<ul>
<li><strong>a) To establish a connection from the attacker's machine to the victim's machine.</strong> - This is the definition of the attack</li>
<li>b) To encrypt data transmitted between the attacker and the victim.</li>
<li>c) To exploit a vulnerability in a web application.</li>
<li>d) To perform a denial-of-service (DoS) attack on the target network.</li>
</ul>
<h3 id="anomaly-37-trivia-09">Anomaly 37: Trivia 09<a class="heading-anchor" href="#anomaly-37-trivia-09" aria-label="Copy link to “Anomaly 37: Trivia 09”">#</a></h3>
<p>In a penetration testing scenario, what is the primary objective of post-exploitation activities after gaining initial access to a target system?</p>
<ul>
<li>a) Deleting system logs to cover tracks - Not something we should do in a pentest we don't want to destroy our customer's network</li>
<li><strong>b) Extracting sensitive data from the compromised system</strong></li>
<li>c) Installing backdoors for persistent access - Not really needed in a pentest</li>
<li>d) Patching vulnerabilities to secure the system - That is not our job we need to find vulns</li>
</ul>
<h3 id="anomaly-38-trivia-10">Anomaly 38: Trivia 10<a class="heading-anchor" href="#anomaly-38-trivia-10" aria-label="Copy link to “Anomaly 38: Trivia 10”">#</a></h3>
<p>In a penetration test, which of the following techniques would be most effective for escalating privileges after gaining initial access to a target system?</p>
<ul>
<li>a) Conducting a brute-force attack against system services. - Not the easiest way to get priv esc</li>
<li>b) Exploiting misconfigured network shares to gain access to sensitive files. - We are not after files</li>
<li>c) Leveraging a zero-day vulnerability to execute arbitrary code. - Rarely would you even know of a zero day or even use one, especially as a pen tester</li>
<li><strong>d) Exploiting a known vulnerability to escalate privileges via a privilege escalation exploit.</strong></li>
</ul>
<h3 id="anomaly-43-trivia-15">Anomaly 43: Trivia 15<a class="heading-anchor" href="#anomaly-43-trivia-15" aria-label="Copy link to “Anomaly 43: Trivia 15”">#</a></h3>
<p>Which of the following is a key component of a firewall?</p>
<ul>
<li>a) Intrusion Detection System (IDS)</li>
<li>b) Anti-virus software</li>
<li>c) Router</li>
<li><strong>d) Access Control List (ACL)</strong> - Controlling access is their primary job</li>
</ul>
<h3 id="anomaly-44-trivia-16">Anomaly 44: Trivia 16<a class="heading-anchor" href="#anomaly-44-trivia-16" aria-label="Copy link to “Anomaly 44: Trivia 16”">#</a></h3>
<p>What is the main advantage of using asymmetric encryption over symmetric encryption?</p>
<ul>
<li>a) Faster encryption and decryption speed - Not true</li>
<li>b) Requires only one key for both encryption and decryption - Not true</li>
<li><strong>c) Provides better security for key distribution</strong> - Breaks up keys</li>
<li>d) Less complex implementation</li>
</ul>
<h3 id="anomaly-45-trivia-17">Anomaly 45: Trivia 17<a class="heading-anchor" href="#anomaly-45-trivia-17" aria-label="Copy link to “Anomaly 45: Trivia 17”">#</a></h3>
<p>What type of attack attempts to deceive users into visiting malicious websites by redirecting them from legitimate websites?</p>
<ul>
<li>a) Man-in-the-Middle (MitM)</li>
<li>b) Cross-Site Scripting (XSS)</li>
<li><strong>c) Phishing</strong></li>
<li>d) SQL Injection</li>
</ul>
<h3 id="anomaly-46-trivia-18">Anomaly 46: Trivia 18<a class="heading-anchor" href="#anomaly-46-trivia-18" aria-label="Copy link to “Anomaly 46: Trivia 18”">#</a></h3>
<p>What is the purpose of a VPN (Virtual Private Network)?</p>
<ul>
<li>a) To hide IP addresses</li>
<li><strong>b) To secure network traffic over public networks</strong></li>
<li>c) To encrypt files on a local computer</li>
<li>d) To prevent malware infections</li>
</ul>
<h3 id="anomaly-47-trivia-19">Anomaly 47: Trivia 19<a class="heading-anchor" href="#anomaly-47-trivia-19" aria-label="Copy link to “Anomaly 47: Trivia 19”">#</a></h3>
<p>Which of the following is NOT a common authentication factor?</p>
<ul>
<li>a) Password</li>
<li>b) Biometrics</li>
<li>c) Security Token</li>
<li><strong>d) Public Key</strong> - Usually a private key</li>
</ul>
<h2 id="analyze">Analyze<a class="heading-anchor" href="#analyze" aria-label="Copy link to “Analyze”">#</a></h2>
<h3 id="anomaly-05-11-gurls-just-want-to-have-fun">Anomaly 05-11: gURLs Just Want to Have Fun<a class="heading-anchor" href="#anomaly-05-11-gurls-just-want-to-have-fun" aria-label="Copy link to “Anomaly 05-11: gURLs Just Want to Have Fun”">#</a></h3>
<h4 id="part-0">Part 0<a class="heading-anchor" href="#part-0" aria-label="Copy link to “Part 0”">#</a></h4>
<p>So we are given a history file from a browser. This is in a SQLite database format so I will open it up with my SQLite data viewer.</p>
<p><img src="/writeups/cyberforce-adventure-2024/history-urls-table.webp" alt="DB Browser for SQLite showing the urls table of the browser history database" decoding="async" loading="lazy" width="1039" height="235"></p>
<p>I am looking at the table called <code>urls</code> for most of the parts.</p>
<h4 id="part-1">Part 1<a class="heading-anchor" href="#part-1" aria-label="Copy link to “Part 1”">#</a></h4>
<p>We are looking for a download so let's search for the word download.</p>
<p><img src="/writeups/cyberforce-adventure-2024/history-search-download.webp" alt="urls table filtered for download, including Google Drive virus scan warning entries" decoding="async" loading="lazy" width="1064" height="411"></p>
<p>One entry catches my eye because in the title we see virus. Using the URL as the flag we get <code>https://drive.usercontent.google.com/download?id=1BfPZslNaHvz3olaV8_3I6ppA_IbdlHLr&#x26;export=download</code></p>
<h4 id="part-2">Part 2<a class="heading-anchor" href="#part-2" aria-label="Copy link to “Part 2”">#</a></h4>
<p>We are now looking for downloads so let's switch to the <code>downloads</code> table and see if we can find a quote.</p>
<p><img src="/writeups/cyberforce-adventure-2024/downloads-table-quote.webp" alt="downloads table with the BrainyQuote quote of the day tab URL highlighted" decoding="async" loading="lazy" width="551" height="249"></p>
<p>We see a quote of the day site, so now let's look at the <code>downloads_url_chains</code> table to see if we can get a full URL.</p>
<p><img src="/writeups/cyberforce-adventure-2024/downloads-url-chains.webp" alt="downloads_url_chains table with the full BrainyQuote image URL highlighted" decoding="async" loading="lazy" width="924" height="294"></p>
<p>Putting that URL into our browser we see the quote.</p>
<p><img src="/writeups/cyberforce-adventure-2024/brainyquote-quote.webp" alt="BrainyQuote image: Change your life today. Don&#x27;t gamble on the future, act now, without delay. - Simone de Beauvoir" decoding="async" loading="lazy" width="1225" height="655"></p>
<h4 id="part-3">Part 3<a class="heading-anchor" href="#part-3" aria-label="Copy link to “Part 3”">#</a></h4>
<p>We are now looking for flights so we are back in the <code>urls</code> table searching for flights.</p>
<p><img src="/writeups/cyberforce-adventure-2024/history-search-flights.webp" alt="urls table filtered for flights showing Southwest booking URLs" decoding="async" loading="lazy" width="1600" height="218"></p>
<p>We see a Southwest page with a to and from in the URL request.</p>
<p><img src="/writeups/cyberforce-adventure-2024/southwest-mht-to-mdw.webp" alt="Southwest booking page: Depart MHT (Manchester, NH) to MDW (Chicago Midway, IL)" decoding="async" loading="lazy" width="494" height="194"></p>
<p>Checking it out shows us the state: <code>New Hampshire</code></p>
<h4 id="part-4">Part 4<a class="heading-anchor" href="#part-4" aria-label="Copy link to “Part 4”">#</a></h4>
<p>Searching for weather in the <code>urls</code> table.</p>
<p><img src="/writeups/cyberforce-adventure-2024/history-search-weather.webp" alt="urls table filtered for weather showing a Shelburne, VT forecast" decoding="async" loading="lazy" width="819" height="201"></p>
<p>This shows us the city right in the title: <code>Shelburne</code></p>
<h4 id="part-5">Part 5<a class="heading-anchor" href="#part-5" aria-label="Copy link to “Part 5”">#</a></h4>
<p>Searching for GitHub.</p>
<p><img src="/writeups/cyberforce-adventure-2024/history-search-github.webp" alt="urls table filtered for github, including the tukaani-project/xz repository" decoding="async" loading="lazy" width="1050" height="610"></p>
<p>On this list the xz repo stands out to me since I know they had a big news piece. Looking up the associated CVE turns out to be the flag.</p>
<h4 id="part-6">Part 6<a class="heading-anchor" href="#part-6" aria-label="Copy link to “Part 6”">#</a></h4>
<p>This one again we are going to search for the title.</p>
<p><img src="/writeups/cyberforce-adventure-2024/history-search-10-cfr.webp" alt="urls table filtered for 10 CFR showing eCFR pages" decoding="async" loading="lazy" width="808" height="215"></p>
<p>Taking note of the id we can look it up in the <code>visits</code> table, putting the id in for <code>url</code>.</p>
<p><img src="/writeups/cyberforce-adventure-2024/visits-table-url-342.webp" alt="visits table filtered to url id 342" decoding="async" loading="lazy" width="587" height="130"></p>
<h4 id="part-7">Part 7<a class="heading-anchor" href="#part-7" aria-label="Copy link to “Part 7”">#</a></h4>
<p>We will search for art.</p>
<p><img src="/writeups/cyberforce-adventure-2024/history-search-art.webp" alt="urls table filtered for art showing National Gallery of Art pages" decoding="async" loading="lazy" width="946" height="866"></p>
<p>I notice one gov site stands out: the National Gallery of Art.</p>
<p><img src="/writeups/cyberforce-adventure-2024/nga-ground-swell.webp" alt="National Gallery of Art page for Edward Hopper&#x27;s Ground Swell, 1939" decoding="async" loading="lazy" width="1232" height="608"></p>
<p>Going to the URL of the painting that wasn't in the downloads table gives us the last name.</p>
<h3 id="anomaly-12-15-while-my-waf-gently-weeps">Anomaly 12-15: While My WAF Gently Weeps<a class="heading-anchor" href="#anomaly-12-15-while-my-waf-gently-weeps" aria-label="Copy link to “Anomaly 12-15: While My WAF Gently Weeps”">#</a></h3>
<h4 id="preface">Preface<a class="heading-anchor" href="#preface" aria-label="Copy link to “Preface”">#</a></h4>
<p>I am loading these logs into Excel, but you can use any Snort log tool of your choosing.</p>
<h4 id="anomaly-12">Anomaly 12<a class="heading-anchor" href="#anomaly-12" aria-label="Copy link to “Anomaly 12”">#</a></h4>
<p>One of the columns has this interesting alert attached to it. Sorting by that reveals a couple of IP addresses.</p>
<p><img src="/writeups/cyberforce-adventure-2024/snort-alert-rows.webp" alt="Snort log rows in Excel sorted by the alert column" decoding="async" loading="lazy" width="1330" height="456"></p>
<p>Sorting and removing duplicates plus combining leaves me with</p>
<p><img src="/writeups/cyberforce-adventure-2024/combined-ip-list.webp" alt="Deduplicated IP addresses combined into a comma-separated list" decoding="async" loading="lazy" width="827" height="305"></p>
<h4 id="anomaly-13">Anomaly 13<a class="heading-anchor" href="#anomaly-13" aria-label="Copy link to “Anomaly 13”">#</a></h4>
<p>So we know the return status was a 200, so let's sort by that first. Then we see that it is using a site called MeterWise Analytics; let's try filtering that down.</p>
<p><img src="/writeups/cyberforce-adventure-2024/meterwise-user-agents.webp" alt="Log rows with status 200 filtered to MeterWise Analytics user agents" decoding="async" loading="lazy" width="1594" height="285"></p>
<p>In my column I filter for MeterWise Analytics and see a few options, but one stands out. Having to re-add the one to the user agent gets us the flag.</p>
<h4 id="anomaly-14">Anomaly 14<a class="heading-anchor" href="#anomaly-14" aria-label="Copy link to “Anomaly 14”">#</a></h4>
<p>So we need to filter based on SQL injection, so I am looking for the word OR in the URI.</p>
<p><img src="/writeups/cyberforce-adventure-2024/sqli-login-requests.webp" alt="Log rows filtered to login requests containing OR 1 SQL injection" decoding="async" loading="lazy" width="1104" height="226"></p>
<p>There is the latest one. In Excel I had to put it in the other time format.</p>
<h4 id="anomaly-15">Anomaly 15<a class="heading-anchor" href="#anomaly-15" aria-label="Copy link to “Anomaly 15”">#</a></h4>
<p>We are looking for path traversal, so we need to filter based on <code>../</code></p>
<p><img src="/writeups/cyberforce-adventure-2024/path-traversal-requests.webp" alt="Log rows filtered to ../ path traversal requests" decoding="async" loading="lazy" width="1455" height="70"></p>
<p>One line doesn't have a Snort alert attached to it.</p>
<h3 id="anomaly-17-ste-what-graphy-v2">Anomaly 17: Ste-what-graphy v2<a class="heading-anchor" href="#anomaly-17-ste-what-graphy-v2" aria-label="Copy link to “Anomaly 17: Ste-what-graphy v2”">#</a></h3>
<p><img src="/writeups/cyberforce-adventure-2024/save-email-attachment.webp" alt="Saving the Message.html email attachment into the Tools folder" decoding="async" loading="lazy" width="961" height="1045"></p>
<p>After opening the email in Thunderbird and extracting the attachment into the tools folder:</p>
<p><img src="/writeups/cyberforce-adventure-2024/shorthand-message.webp" alt="The attachment: a line of shorthand above an image of a locked envelope" decoding="async" loading="lazy" width="1600" height="950"></p>
<p>We see some shorthand and a script that claims to be able to read it. Taking a ss of the image and running it through the script will pop a window with potential matches. I had to change the threshold to .8 to not keep getting false positives.</p>
<p><img src="/writeups/cyberforce-adventure-2024/shorthand-01-operate.webp" alt="Shorthand match: operate" decoding="async" loading="lazy" width="164" height="55"> <img src="/writeups/cyberforce-adventure-2024/shorthand-02-with.webp" alt="Shorthand match: with" decoding="async" loading="lazy" width="191" height="43"> <img src="/writeups/cyberforce-adventure-2024/shorthand-03-extreme.webp" alt="Shorthand match: extreme" decoding="async" loading="lazy" width="164" height="36"> <img src="/writeups/cyberforce-adventure-2024/shorthand-04-precaution.webp" alt="Shorthand match: precaution" decoding="async" loading="lazy" width="162" height="52"> <img src="/writeups/cyberforce-adventure-2024/shorthand-05-find.webp" alt="Shorthand match: find" decoding="async" loading="lazy" width="140" height="45"> <img src="/writeups/cyberforce-adventure-2024/shorthand-06-secret.webp" alt="Shorthand match: secret" decoding="async" loading="lazy" width="188" height="46"> <img src="/writeups/cyberforce-adventure-2024/shorthand-07-with.webp" alt="Shorthand match: with" decoding="async" loading="lazy" width="164" height="42"> <img src="/writeups/cyberforce-adventure-2024/shorthand-08-password.webp" alt="Shorthand match: password" decoding="async" loading="lazy" width="182" height="84"> <img src="/writeups/cyberforce-adventure-2024/shorthand-09-whole-hearted.webp" alt="Shorthand match: whole-hearted" decoding="async" loading="lazy" width="201" height="47"></p>
<p><img src="/writeups/cyberforce-adventure-2024/aperisolve-password.webp" alt="Aperi&#x27;Solve upload of the envelope image with the password whole-hearted" decoding="async" loading="lazy" width="759" height="773"></p>
<p>Putting the image into <a href="https://www.aperisolve.com">AperiSolve</a> with the password:</p>
<p><img src="/writeups/cyberforce-adventure-2024/aperisolve-steghide.webp" alt="Aperi&#x27;Solve steghide result: wrote extracted data to steganopayload819210.txt" decoding="async" loading="lazy" width="1102" height="235"></p>
<p>Steghide finds the data and inside is the flag.</p>
<h2 id="oversee-and-govern">Oversee and Govern<a class="heading-anchor" href="#oversee-and-govern" aria-label="Copy link to “Oversee and Govern”">#</a></h2>
<h3 id="anomaly-29-trivia-01">Anomaly 29: Trivia 01<a class="heading-anchor" href="#anomaly-29-trivia-01" aria-label="Copy link to “Anomaly 29: Trivia 01”">#</a></h3>
<p>Which of the following is a technique used in post-exploitation to maintain persistent access to a compromised system without relying on traditional backdoors or implants?</p>
<ul>
<li><strong>c) Scheduled task creation</strong> - This is correct because it is not something traditional and has to be created by you</li>
<li>a) Kernel patching - This is incorrect because it would rely on a traditional backdoor</li>
<li>b) Rootkit installation - This is a traditional method</li>
<li>d) DLL hijacking - Not known to create persistence</li>
</ul>
<h3 id="anomaly-16-con365nt-is-key">Anomaly 16: COn365nt is Key<a class="heading-anchor" href="#anomaly-16-con365nt-is-key" aria-label="Copy link to “Anomaly 16: COn365nt is Key”">#</a></h3>
<h4 id="request-1">Request 1<a class="heading-anchor" href="#request-1" aria-label="Copy link to “Request 1”">#</a></h4>
<p>Pass everything <code>Approved</code></p>
<h4 id="request-2">Request 2<a class="heading-anchor" href="#request-2" aria-label="Copy link to “Request 2”">#</a></h4>
<p>Since this app wants to sign in and when googling I can't find anything about conditional access, it fails <code>DENIED:7</code></p>
<h4 id="request-3">Request 3<a class="heading-anchor" href="#request-3" aria-label="Copy link to “Request 3”">#</a></h4>
<p>This request wants the permission <code>Read and write all groups</code> which is in the ban list <code>BLOCK:4</code></p>
<h4 id="request-4">Request 4<a class="heading-anchor" href="#request-4" aria-label="Copy link to “Request 4”">#</a></h4>
<p>This request is not an appropriate type because of a separate email sync <code>DENIED:2</code></p>
<h4 id="request-5">Request 5<a class="heading-anchor" href="#request-5" aria-label="Copy link to “Request 5”">#</a></h4>
<p>I cannot seem to find its app ID in the system or Google <code>DENIED:3</code></p>
<h4 id="request-6">Request 6<a class="heading-anchor" href="#request-6" aria-label="Copy link to “Request 6”">#</a></h4>
<p>Since this app wants to read full profiles which can contain data subject to breach notification laws, it fails nine <code>BLOCKED:9</code></p>
<h2 id="energy">Energy<a class="heading-anchor" href="#energy" aria-label="Copy link to “Energy”">#</a></h2>
<h3 id="anomaly-39-trivia-11">Anomaly 39: Trivia 11<a class="heading-anchor" href="#anomaly-39-trivia-11" aria-label="Copy link to “Anomaly 39: Trivia 11”">#</a></h3>
<p>The Department of Energy supports advancements in sustainable transportation. Which of the following is NOT a focus area?</p>
<ul>
<li>a. Electric vehicle (EV) infrastructure</li>
<li>b. Biofuel production</li>
<li>c. Hydrogen fuel cells</li>
<li><strong>d. Lead-acid battery research for EVs</strong> - We are trying new stuff</li>
</ul>
<h3 id="anomaly-40-trivia-12">Anomaly 40: Trivia 12<a class="heading-anchor" href="#anomaly-40-trivia-12" aria-label="Copy link to “Anomaly 40: Trivia 12”">#</a></h3>
<p>Which U.S. agency launched the "Building a Better Grid" initiative to expand and modernize the nation's electric grid?</p>
<ul>
<li>a. Environmental Protection Agency (EPA)</li>
<li><strong>b. Department of Energy (DOE)</strong> - <a href="https://www.energy.gov/gdo/building-better-grid-initiative">https://www.energy.gov/gdo/building-better-grid-initiative</a></li>
<li>c. Federal Energy Regulatory Commission (FERC)</li>
<li>d. Department of Defense (DOD)</li>
</ul>
<h3 id="anomaly-41-trivia-13">Anomaly 41: Trivia 13<a class="heading-anchor" href="#anomaly-41-trivia-13" aria-label="Copy link to “Anomaly 41: Trivia 13”">#</a></h3>
<p>Which innovative technology was the Department of Energy exploring to enhance grid resilience against cyber-attacks in 2023 according to their GMI calls?</p>
<ul>
<li>a. Blockchain technology</li>
<li><strong>b. Quantum encryption communications</strong> - <a href="https://www.energy.gov/gmi/2023-grid-modernization-lab-call#:~:text=Topic%20Area%203%3A%20Quantum%20Facilities%20for%20Computing%2C%20Sensing%2C%20and%20Security.%C2%A0This%20topic%20focuses%20on%20leveraging%20existing%20and%20near%2Dterm%20quantum%20computing%2C%20sensing%2C%20and%20security%20technologies%20to%20address%20the%20grid%27s%20vulnerabilities%20and%20the%20grid%27s%20increasing%20complexity.%C2%A0">Link to energy.gov report</a></li>
<li>c. AI threat detection systems</li>
<li>d. Satellite grid monitoring</li>
</ul>
<h3 id="anomaly-42-trivia-14">Anomaly 42: Trivia 14<a class="heading-anchor" href="#anomaly-42-trivia-14" aria-label="Copy link to “Anomaly 42: Trivia 14”">#</a></h3>
<p>What should be the primary objective of a risk management strategy? CISM</p>
<ul>
<li>a. Determine the organization's risk appetite. - This is just being lazy</li>
<li>b. Identify credible risks and transfer them to an external party. - We should not just give away all the risk</li>
<li><strong>c. Identify credible risks and reduce them to an acceptable level.</strong></li>
<li>d. Eliminate credible risks. - We can never eliminate risks</li>
</ul>
<h3 id="anomaly-48-trivia-20">Anomaly 48: Trivia 20<a class="heading-anchor" href="#anomaly-48-trivia-20" aria-label="Copy link to “Anomaly 48: Trivia 20”">#</a></h3>
<p>Based on the 2023 U.S. Energy Information Administration, this was the leading renewable energy source.</p>
<ul>
<li>a. Solar</li>
<li>b. Hydropower</li>
<li><strong>c. Wind</strong> - <a href="https://www.eia.gov/outlooks/aeo/narrative/index.php#ExecutiveSummary:~:text=Top-,The%20Electricity%20Mix%20in%20the%20United%20States%20Shifts%20from%20Fossil%20Fuels%20to%20Renewables,-In%20this%20section">Link</a></li>
<li>d. Geothermal</li>
</ul>
<h2 id="investigate">Investigate<a class="heading-anchor" href="#investigate" aria-label="Copy link to “Investigate”">#</a></h2>
<h3 id="anomaly-49-knock-knockwhos-there">Anomaly 49: Knock Knock…Who’s There?<a class="heading-anchor" href="#anomaly-49-knock-knockwhos-there" aria-label="Copy link to “Anomaly 49: Knock Knock…Who’s There?”">#</a></h3>
<p>We have a list of encoded text in Base64 and hex; I will convert each back into ASCII.</p>
<p><img src="/writeups/cyberforce-adventure-2024/knock-hex-message.webp" alt="Text file containing one of the hex-encoded messages" decoding="async" loading="lazy" width="1358" height="124"></p>
<p><img src="/writeups/cyberforce-adventure-2024/cyberchef-from-hex.webp" alt="CyberChef From Hex decoding the message to plain text" decoding="async" loading="lazy" width="1600" height="601"></p>
<h3 id="anomaly-50-sloggin-through-these-logins">Anomaly 50: Sloggin through these logins<a class="heading-anchor" href="#anomaly-50-sloggin-through-these-logins" aria-label="Copy link to “Anomaly 50: Sloggin through these logins”">#</a></h3>
<p>Looking at the log file we notice James's account having many incorrect creds: <code>James</code></p>]]></content:encoded>
        </item>
    </channel>
</rss>