<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Fidget-Cube's Security Blog</title>
    <subtitle>Application security research reports and CTF challenge walkthroughs (and maybe some other stuff)</subtitle>
    <link rel="self" href="https://fidgetcube.dev/feed.php" type="application/atom+xml"/>
    <link rel="alternate" href="https://fidgetcube.dev" type="text/html"/>
    <updated>2025-09-26T21:39:01Z</updated>
    <author>
        <name>Max vonBlankenburg</name>
    </author>
    <id>https://fidgetcube.dev</id>
    <icon>https://fidgetcube.dev/img/favicon.ico</icon>
    <entry>
        <title>Return to the Woods</title>
        <link href="https://fidgetcube.dev/index.php?page=post&amp;name=return_to_the_woods" rel="alternate" type="text/html"/>
        <published>2025-09-26T21:39:01Z</published>
        <updated>2025-09-26T21:39:01Z</updated>
        <id>https://fidgetcube.dev/index.php?page=post&amp;name=return_to_the_woods</id>
        <content type="html">
            <h1>Return to the Woods</h1>
<p>Taken from my backpacking journal, entry dated to September 24th, written at a camp on the edge of a lake in the Sierras of the western USA.</p>
<p>Like many people, I love to go hiking and backpacking. And camping. For most people it's pretty obvious why it's enjoyable, and why it isn't. You get to experience natural beauty. You don't have to worry about anything except your own survival. You can be alone and free with your thoughts. It's also difficult, dirty and uncomfortable. You have to forgo many of the common conveniences we've grown accostomed to.</p>
<p>For a long time I thought the wilderness was where I belonged. I was in the boy scouts through childhood (highly recommend, I think they accept more than just boys now too), and they taught me how to survive in the wilderness (editing note: they only teach the basics, you'll need proper training to actually "survive" on your own). How to start a fire, how to perform first aid, valuable outdoor knowledge. Whenever I went on trips with them, I felt refreshed and at home, as if the weight of acting as a "human being" in a "civilized society" was lifted off my shoulders. In the wilderness, everything you need to worry about is right there in front of you, and if something is wrong, you know right away (editing note: that doesn't always mean the mistakes you make have immediate consequences, be prepared!).</p>
<p>Eventually I grew up and left the scouts, and got too busy with college and work to take trips of any kind. I didn't like it, nature is therapudic, and I needed that in my life. Eventually (now, really) I got to have enough freedom to start car camping and backpacking again, and it's good to be back.</p>
<p>A lot of people take long journeys out into the wilderness alone to "find themselves". Others give up on society entirely, preferring instead to live more naturally, closer to how our ancestors lived. I've considered doing these things myself. But, staying out here for too long, feeling the emptiness take hold, I've had to ask myself if I'm "getting away" from it all or if I'm "running away" from it. There are many things I love about the modern world - the computers, the music, urban ruins, and perhaps most importantly, other humans. As much as we may want to return to our roots as hunter-gatherers, it's hard to find a community that way (editing note: or maybe I'm just not cut out for it lol).</p>
<p>So I stay in the middle between two worlds. Between the technological hell that destroys us, and the natural world left over once we're all gone.</p>
<!-- Categories:Camping -->
        </content>
        <summary type="html">
            <p>Taken from my backpacking journal, entry dated to September 24th, written at a camp on the edge of a lake in the Sierras of the western USA.</p>
        </summary>
        <author>
            <name>Max vonBlankenburg</name>
        </author>
        <category>Camping</category>
    </entry>
    <entry>
        <title>3ds Modding 101</title>
        <link href="https://fidgetcube.dev/index.php?page=post&amp;name=3ds_modding_101" rel="alternate" type="text/html"/>
        <published>2025-06-13T17:42:41Z</published>
        <updated>2025-06-13T17:42:41Z</updated>
        <id>https://fidgetcube.dev/index.php?page=post&amp;name=3ds_modding_101</id>
        <content type="html">
            <h1>3ds Modding 101</h1>
<p>Still have one of these old things? Nintendo might not make them anymore, but they're still useful.</p>
<p><img src="https://www.giantbomb.com/a/uploads/original/0/5150/1686077-3dshw11908.jpg" alt="3ds"/></p>
<p>With rapidly innovating game markets, there's no room anymore for a console that just exists to be used indefinitely. No "blue ocean" business model, just an endless supply of new consoles to replace the old. And with the Switch 2 finally coming out, I went back to the only Nintendo console I still own. A Nintendo 3ds, obtained day 1 at Target in 2011.</p>
<p>Turns out I still had some pretty good games for it. Star Fox, Ocarina of Time, Fire Emblem. But there were also a lot I remember wanting to play, but never got around to. Games like Majora's Mask and Shovel Knight. I also had misplaced my old copy of Mariokart 7. Looking online, some of these games can be worth hundreds of dollars to buy, did I miss my shot at ever experiencing these games for myself?</p>
<p>No. Thanks to archival websites like <a href="https://vimm.net/links">Vimm's Lair</a> and <a href="https://www.emuparadise.me/roms-isos-games.php">Emuparadise</a> we still have access to these games in the form of ROMs - digital copies of video game cartridges. These ROMs can be run on emulators such as Citra and Dolphin. But that's only possible to do on a supported computer; how do we download these files onto our 3ds and play them on the original hardware?</p>
<p>Introducing... 3ds jailbreaking! <a href="https://3ds.hacks.guide/">https://3ds.hacks.guide/</a></p>
<p>Jailbreaking your 3ds allows you to install custom firmware on the device and gives you full control of all the device internals. There are easy guides you can follow online, and helpful folks in the forums as well. In addition to allowing you to install 3ds ROMs off the internet, you can also...</p>
<ul>
<li>Customize your home theme!</li>
<li>Edit, backup and restore save data!</li>
<li>Install cheats!</li>
<li>Install custom software built for the 3ds, like messenger apps or video clients!</li>
<li>Install mods for existing games!</li>
</ul>
<p>And so much more!</p>
<p>In addition to the main draw of playing all your favorite discontinued 3ds games, there are two other major reasons I personally wanted to mod my 3ds. The first was console emulation. The truth is, the 3ds can emulate way more than just 3ds games.</p>
<p>You're probably already familiar with the fact that you can play classic DS games on your 3ds as well, simply by plugging in the game cartridge. Naturally, there's an emulator for these games too. And that's not all! Community developers have integrated emulators from many more classic consoles, including the NES, SNES, Gameboy and Gameboy Advance. You can play games never even ported to the 3ds in the first place! I personally recommend <a href="https://wiki.ds-homebrew.com/twilightmenu/installing-3ds">TWiLight Menu++</a> for a nice suite of emulators.</p>
<p>The second reason I had for jailbreaking my 3ds was less out of curiosity and more out of necessity. On April 8th, 2024, Nintendo took down all online services supporting the 3ds. That includes, perhaps most relevantly, online multiplayer and the eshop. It was a long run, but ultimately the moustached man decided we weren't profitable anymore and cut us off. 3ds players have become completely isolated.</p>
<p>As if. As long as self-hosting is legal, fans will run their own servers, that much has been made clear. There's a Nintendo network alternative currently running called the <a href="https://pretendo.network/">Pretendo network</a>. If you're lucky, their servers wont have been taken down by Nintendo by the time of reading, and if so, I sincerely hope there is some other alternative out there. These servers bring online multiplayer back for all supported games, so all you need to take care of is finding active players.</p>
<p>Ultimately, I just wanted to plug all these dedicated creators making content for a console released over a decade ago, y'all are really amazing. As for you, dear reader, what are you waiting for? Dust off that old console of yours and give it some love.</p>
<!-- Categories:Games -->
        </content>
        <summary type="html">
            <p>Still have one of these old things? Nintendo might not make them anymore, but they're still useful.</p>
        </summary>
        <author>
            <name>Max vonBlankenburg</name>
        </author>
        <category>Games</category>
    </entry>
    <entry>
        <title>How to Gain Code Execution on a C Binary using a Buffer Overflow, Part 1</title>
        <link href="https://fidgetcube.dev/index.php?page=post&amp;name=rop_part_1" rel="alternate" type="text/html"/>
        <published>2024-08-05T21:36:51Z</published>
        <updated>2024-08-05T21:36:51Z</updated>
        <id>https://fidgetcube.dev/index.php?page=post&amp;name=rop_part_1</id>
        <content type="html">
            <h1>How to Gain Code Execution on a C Binary using a Buffer Overflow, Part 1</h1>
<p>So, you're playing a CTF and you decide to try the <code>pwn</code> category for the first time. You're given a file with a weird or no extension, and a string that looks something like <code>nc mydoma.in 12345</code>. You figure out what <a href="https://linuxize.com/post/netcat-nc-command-with-examples/">NetCat</a> is and you connect to the server, which asks you for some input and kicks you out no matter what you enter. You've never seen a challenge like this before.</p>
<p>Allow me to introduce you to the Buffer Overflow vulnerability. This is one of the most well known and commonly exploited vulnerabilities in the world, or at the very least, used to be. Ever since the famous paper was released, <a href="https://www.eecs.umich.edu/courses/eecs588/static/stack_smashing.pdf">Smashing The Stack For Fun And Profit</a>, no C program has been safe. This vulnerability allows an attacker to functionally run whatever code they want on a running C program that accepts user input, and doesn't properly limit the length of that input.</p>
<p>There have been many summaries and writeups of this kind of problem, as well as variations of it, online. I'll give a special shoutout to <a href="https://www.rapid7.com/blog/post/2019/02/19/stack-based-buffer-overflow-attacks-what-you-need-to-know/">Rapid7's explanation</a>, as they go into the practical nature of this vulnerability and cover many of the same things this blog post touches on as well. For right now, I want to return to the original question. Say you're playing a CTF and you come across a challenge that requires you to exploit this vulnerability. How do you do it <em>right</em>?</p>
<p>Let's get right into it. These challenges will typically give you two things. The first is an executable binary file compiled from the C programming language. This is the file with the weird extension you may have encountered. This file can be run in a standard Linux command line (or a Mac terminal, or in WSL for Windows). Simply make it executable and run it with the following commands:</p>
<p><code>chmod +x challenge-file-name &amp;&amp; ./challenge-file-name</code></p>
<p>Once you run it, you should see some output in the terminal that looks strangely similar. Now look at the second thing the challenge gives you: a connection string. This string gives you all the information you need to connect to a remote server on the internet. Most commonly, this is a NetCat command that looks something like this:</p>
<p><code>nc &lt;ip address or domain&gt; &lt;port number&gt;</code> e.g. <code>nc 12.34.567.89 1234</code></p>
<p>Go ahead and type this string into your terminal and run it. Notice how it gives the exact same output as when you ran the executable binary? That's because it's the exact same program. Your goal for this challenge is to connect to this remote server and exploit the program running on it, and you're given this exact program so you can analyze the code and test your exploit.</p>
<p>So now that we know exactly what we're being given, it's time to analyze the executable binary. What's <em>actually</em> being run on this server?</p>
<p>There are many binary analysis tools out there you can use. In this tutorial we will use the Gnu Debugger, or gdb. This is a command line tool that comes pre-installed on most Linux distributions, and there's even an <a href="https://www.onlinegdb.com/">online version</a>! (though you do need the source code for this version)</p>
<p>To start analyzing the challenge binary, run the following commmand:</p>
<p><code>gdb challenge-file-name</code></p>
<p><img src="/img/gdb-1.png" alt="gdb-screenshot-1"/></p>
<p>So, we know that this program accepts some form of input from a user, but that doesn't necessarily mean that it has a buffer overflow vulnerability. Let's take a look at the code itself to confirm for sure that it is vulnerable. Unfortunately, we aren't given the exact C source code for this program, but we <em>can</em> look at something similar and more lower-level. It's called <a href="https://www.cs.virginia.edu/~evans/cs216/guides/x86.html">x86 Assembly Language</a>:</p>
<p><img src="/img/gdb-2.png" alt="gdb-screenshot-2"/></p>
<p>Assembly languages of all kinds are very complicated to understand and analyze, it's fine if you don't understand anything you see in the picture above. While I do recommend studying up on what assembly language is and how it works, that wont be necessary for this guide. For now, just understand that this is the representation of the program that your computer <em>actually</em> sees and understands, and knows how to execute. And it can be analyzed just like any other language.</p>
<p>Going back to the picture above, we are specifically looking at the assembly code for the "main" function. On some of the lines, in red text and enclosed in angle brackets &lt;&gt;, are function names like "printf@plt", "fgets@plt" and "strcmp@plt". These are standard C library functions. "fgets" in particular is a function that accepts input, and can potentially be vulnerable to buffer overflow. Let's try it out! To run the program, enter the <code>run</code> command in gdb, and when it asks for your input, put in like 100 random characters.</p>
<p><img src="/img/gdb-3.png" alt="gdb-screenshot-3"/></p>
<p>Bingo! Our input caused a Segmentation Fault! If the program handled our input correctly it would have just output "incorrect password", but instead something inside broke! Let's take a look at what exactly broke. Run the program again, but before you do, run the command <code>break main</code> to make the program stop right before the "main" function.</p>
<p><img src="/img/gdb-4.png" alt="gdb-screenshot-4"/></p>
<p>This is where we have to talk about something called <a href="https://en.wikipedia.org/wiki/Address_space_layout_randomization">Address Space Layout Randomization</a>. You need to understand computer architecture to fully get it, but basically it means that each time you run a program, it is loaded into a random memory space on your computer. We want to look at the program at the exact point in time that we broke it with our input, so we need to have the exact memory address of that point in the code. Go ahead and run <code>disassemble main</code> again and see how the numbers on the left side change.</p>
<p><img src="/img/gdb-5.png" alt="gdb-screenshot-5"/></p>
<p>Now they all have a bunch of 5s! Your numbers are going to be different from mine since they are random memory addresses. This shouldn't matter though, all you have to do now is set a breakpoint at the very last "ret" instruction in the code. Run <code>break *0x0000555555555354</code>, replacing my memory address with the one showing up on the very last line of your assembly code. Then run <code>continue</code> and send it your spaghetti.</p>
<p><img src="/img/gdb-6.png" alt="gdb-screenshot-6"/></p>
<p>Perfect, we've hit the breakpoint. Now run <code>x/s $sp</code> - e(x)amine (s)tring at (s)tack (p)ointer.</p>
<p><img src="/img/gdb-7.png" alt="gdb-screenshot-7"/></p>
<p>There it is! All that spaghetti we inputted. Basically, when we sent a super long string to the program input, it put the first part of it into a variable, but didn't have anywhere to put the rest of it, so it "overflowed" into the program stack!</p>
<p>I'll leave you at this for now. So far, we've confirmed that this program has a buffer overflow vulnerability, have successfully crashed the program, and can see exactly where our overflowed input is being stored. In the next post, I'll touch on how exactly to get that "code execution" promised in the title of this post, so you can actually solve this CTF challenge. Hope this was helpful, and I'll see you in the next one!</p>
<!-- Categories:Reverse Engineering,Exploitation -->
        </content>
        <summary type="html">
            <p>So, you're playing a CTF and you decide to try the <code>pwn</code> category for the first time. You're given a file with a weird or no extension, and a string that looks something like <code>nc mydoma.in 12345</code>. You figure out what <a href="https://linuxize.com/post/netcat-nc-command-with-examples/">NetCat</a> is and you connect to the server, which asks you for some input and kicks you out no matter what you enter. You've never seen a challenge like this before.</p>
        </summary>
        <author>
            <name>Max vonBlankenburg</name>
        </author>
        <category>Reverse Engineering</category>
        <category>Exploitation</category>
    </entry>
    <entry>
        <title>JavaScript is a Joke</title>
        <link href="https://fidgetcube.dev/index.php?page=post&amp;name=javascript_is_a_joke" rel="alternate" type="text/html"/>
        <published>2023-08-27T00:00:00Z</published>
        <updated>2023-08-27T00:00:00Z</updated>
        <id>https://fidgetcube.dev/index.php?page=post&amp;name=javascript_is_a_joke</id>
        <content type="html">
            <h1>JavaScript is a Joke</h1>
<p>He did it! He said the line!</p>
<p>(this is not a serious blog post if you couldn't tell, adult language ahead)</p>
<p>So anyway, it's a month in at my new job at Semgrep. Really fun so far. I get to research new CVEs that come out in open source software, which means I get to see all the new major disasters firsthand. On rare occasions you get to see the sophisticated new hacks against major libraries or whatever, but most of it is just access mismanagement, "isadmin" cookies, awful vuln descriptions and, as you may have guessed, JavaScript acting as the one thing standing between humanity and a prosperous world society. I'm not here to write a cool, detailed description of a CTF challenge solution or sophisticated hack. No, today is all just complaints.</p>
<p>At some point I will do a post outlining some of the most god-awful CVE disclosures I have had the privilege to discover, but we'll save that for later. I have faith in the security research community bringing us quality content to laugh at in the coming months.</p>
<p>So I have three things to bring up. The first is a popular weakness that was discovered in the way JavaScript handles objects: Prototype Pollution. The first of these was disclosed in 2019, and since then researchers have been variant hunting the shit out of it. It's a very fickle vuln, just as likely to be harmless as it is to allow code execution privileges. I'll do my best to explain how it works.</p>
<p>First thing to know is that every JavaScript object <code>{}</code> has a prototype <code>{}.__proto__</code>. This defines the attributes (member functions and variables) the object is created with. Prototypes are inheritable, so if you define a "child" object based off a "parent" object, the child prototype contains all the attributes of the parent prototype as well. Also, every object is inherited from the default object <code>{}</code>. You can probably see where this is going.</p>
<p>If we modify the prototype of the default object and add our own attributes (for example: <code>{}.__proto__.something = "foobar"</code>), those attributes will <strong>automatically be added to any existing objects <em>and</em> any new objects!</strong> Of course JavaScript lets you do this, this is totally fine! Except that this means, of course, <strong>anyone with access to the default object prototype can add whatever they want to every single object in the code</strong>.</p>
<p>Of course, if you code doesn't make assumptions about what attributes are in what objects, you should be fine. But if it does, congratulations, you've given code editing rights to Anonymous.</p>
<p>The second thing I have to bring up is more of a cool fact! A cool fact that could totally crash your Javascript code without you even knowing it existed.</p>
<p><code>{ toString: '' }</code> is impossible to automatically convert to a primitive! Why is this? <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#type_coercion">Type coercion</a>.</p>
<p>In order to automatically convert an object (like <code>{}</code>) to a primitive value (like <code>3</code> or <code>"foobar"</code>), JavaScript tries 3 things:</p>
<ol>
<li>calling the object's <code>[@@toPrimitive]()</code> function, which <code>{ toString: '' }</code> does not have.</li>
<li>calling <code>{ toString: '' }.valueOf()</code>, which returns {} itself, which is not a primitive so it's ignored.</li>
<li>calling <code>{ toString: '' }.toString()</code>, which <em>would</em> return <code>"[object Object]"</code>, but we overwrote the <code>toString</code> method with an empty string instead, so it's no longer a function.</li>
</ol>
<p>If all 3 attempts fail, a TypeError is thrown! This can lead to some cute lil <a href="https://github.com/advisories/GHSA-hhr9-rh25-hvf9">DoS vulns</a>.</p>
<p>Now for the final thing, and this one takes the cake. Have any of y'all used <a href="https://github.com/patriksimek/vm2">vm2</a> before? It's a project attempting to take Node.js' built-in <code>vm</code> module and make it secure! Mainly by attempting to patch every single hole imaginable, and there were a lot. Admittedly, their tenacity and determination was commendable, but ultimately, JavaScript won. I didn't work on the project, so I don't know all the details, but from a little bit of research it appears that Node was intercepting calls inside the vm before they could be proxied or overwritten. That sounds to me like patching every hole in a bucket only for the bucket to tip over.</p>
<p>So vm2 hit a dead end, what's the big deal? Well, the big deal is that <em>everyone uses vm2</em>. If you're looking to easily secure user-run code, vm2 is the go-to solution. Now that it's gone, there is only <em>one</em> project <em>in existence</em> focused on secure virtualization of JavaScript. If isolated-vm goes under, we're fucking toast.</p>
<p>There's also a more in-depth description of that story over on the Semgrep blog, if you want to learn more. I would link it, but that would be a little masturbatory, plus no one reads this blog anyway.</p>
<p>Oh, I got one more as a bonus for reading this far. Friendly reminder for how much garbage there is on npm: <a href="https://www.npmjs.com/package/npm-ghost-726?activeTab=code">https://www.npmjs.com/package/npm-ghost-726?activeTab=code</a></p>
<p>Some goobers wrote a script that literally uploads thousands of copies of the same package to npm. Apparently the goal was to inflate the dependent count on their main node packages, so they would have more credibility and "clout". It's not really harmful, just false advertising I guess. npm is a minefield when it comes to broken, malicous, or downright weird packages.</p>
<p>Anyway, that's all I have for today. Maybe now is a good time to admit that I've been writing it Javascript my entire life up until now. I feel like that should be correct. If the language isn't strongly typed, why should its name be?</p>
<!-- Categories:JavaScript -->
        </content>
        <summary type="html">
            <p>He did it! He said the line!</p>
        </summary>
        <author>
            <name>Max vonBlankenburg</name>
        </author>
        <category>JavaScript</category>
    </entry>
    <entry>
        <title>OMGzip</title>
        <link href="https://fidgetcube.dev/index.php?page=post&amp;name=omgzip" rel="alternate" type="text/html"/>
        <published>2023-05-09T00:00:00Z</published>
        <updated>2023-05-09T00:00:00Z</updated>
        <id>https://fidgetcube.dev/index.php?page=post&amp;name=omgzip</id>
        <content type="html">
            <h1>OMGzip</h1>
<h2>OMG, it's a .zip!</h2>
<blockquote>
<p>solves: 79<br/>
points: 56<br/>
challenge file(s): <a href="https://github.com/Fidget-Cube/write-ups/tree/main/2023/DEFCON_CTF_Qualifiers/OMGzip/omgzip">omgzip</a>, <a href="https://github.com/Fidget-Cube/write-ups/tree/main/2023/DEFCON_CTF_Qualifiers/OMGzip/data.tar.omgzip">data.tar.omgzip</a><br/>
solution file(s): <a href="https://github.com/Fidget-Cube/write-ups/tree/main/2023/DEFCON_CTF_Qualifiers/OMGzip/decompressor.py">decompressor.py</a><br/>
original challenge github: <a href="https://github.com/Nautilus-Institute/quals-2023/tree/main/omgzip">https://github.com/Nautilus-Institute/quals-2023/tree/main/omgzip</a>  </p>
</blockquote>
<p>Just this past weekend, <a href="https://www.meetup.com/pacifichackers/">Pacific Hackers Association</a> and <a href="https://www.meetup.com/hackmiami/">HackMiami</a> joined forces to compete in the <a href="https://quals.2023.nautilus.institute/index.html">DEFCON CTF Qualifiers</a>. The top 12 teams from this CTF go on to play the most competitive and challenging CTF in the world at DEFCON 31. Though, to be fair, our team wasn't of that caliber. We were able to solve 2 challenges (not counting the sanity checks) and finished in 92nd place, which is a great success by my standards! I was able to solve this one with the help of my friend Sam. It's a reverse engineering challenge, but with Python code instead of x86 assembly, which makes things much nicer.</p>
<p>data.tar.omgzip is a .tar file compressed using a custom compression program called omgzip. The Python source code for omgzip is provided, so all you need to do is reverse the logic of the program and write your own decompression program.</p>
<p>From the creator:<br/>
"The original version of this challenge was pretty trivially solvable by ChatGPT, so I made it more difficult before its release and added a bunch of random comments and changed a bunch of variable names to make doing that take more time. We'll see if I was successful or not."</p>
<p>The source code is pretty funny to read through. All the comments are basically worthless, and the creator even makes use of confusing naming conventions and data types (i.e. <code>idx += True</code>) in order to throw off any kind of LLM trying to understand the code logic automatically. Regardless, any human with a decent amount of programming experience shouldn't be thrown off by this as long as they examine the logic of the code itself, it's not overly complicated.</p>
<p>Overall, the compression can be broken down into two steps. First, the compress() function is called, which looks for repeated concurrent bytes in the file and groups them together with a count. This can be reversed with some simple if/else logic, which I've done in uncompress_part1(). The second step calls the encode() method from a Deflator class on the file bytes, which is a bit more complicated.</p>
<p>To understand this second step, you first need to understand the data structure defined by the Family class. For those familiar with data structures, this is a <a href="https://www.geeksforgeeks.org/binary-tree-data-structure/">binary tree</a> implementation. Each node contains an int of data and pointers to its parent and two children. This data structure is used by the Deflater class to map common bytes to shorter bit arrays, which I'll get into later.</p>
<p>When an object of the Deflater class is created, a complete binary tree of depth 8 is created and stored in self.money (see "<a href="https://en.wikipedia.org/wiki/Binary_tree">Types of binary trees</a>"). Each node of the binary tree at depth 8 contains a number. Since the tree is 8 layers deep, there are 256 total nodes at the 8th layer, because 2^8 = 256. The nodes store incremental numbers from "right" to "left", starting with 0 in the rightmost node and ending with 255 in the leftmost. For purposes of visualizing the graph, I'm speaking as if the "successful_firstborn" child is on the right side of the parent, and the "conflicted_stepchild" child is on the left side. Pointers to each of these layer 8 nodes are also stored in self.dictionary, from right (at index "0") to left (at index "255").</p>
<p>In case your head is spinning, here's a visual aid:</p>
<p><img src="https://github.com/Fidget-Cube/write-ups/tree/main/2023/DEFCON_CTF_Qualifiers/OMGzip/bintree.jpg" alt="binary tree"/>.</p>
<p>Luckily you wont have to reverse this setup step; setting up the binary tree will be exactly the same for decompression.</p>
<p>Moving on to the encode() method. This method runs _travesty() on each individual byte of the file. _travesty() plugs this byte into self.dictionary in order to get one of the binary tree nodes at layer 8. From this node, it then travels up the levels of the tree, writing a single bit of data for each layer travelled. If the child node is on the left side of the parent, a 1 is written, and if on the right, a 0 is written. The first time _travesty() is run, this simply writes the binary representation of the input byte, because of the way the tree was set up. Take 0x00 for example. The tree is 8 layers deep, and numbers are ordered from lowest to highest left -&gt; right. 0x00 is on the farthest right node and is on the right side of every parent, so traversing the tree upwards produces 00000000. These bits are then added to the output array in big-endian format (most significant bit on the left), and then _magic() function is called on the starting layer 8 node before returning.</p>
<p>If the _magic() function wasn't called, each call to _travesty() would output the exact byte that was put in, as described above. However, _magic() performs a bit of scrambling on the binary tree on each pass, which makes things complicated. Put simply, _magic() switches the position of the input node with its aunt/uncle node, then does the same to that aunt/uncle's parent node until it reaches the root of the tree. Ultimately this puts the node at a higher layer in the tree, and also makes the tree no longer complete. Fortunately, you don't need to reverse this function, but it is important to understand, because this means our future bytes wont always map to themselves anymore, nor will they always map to 8 bits in the resulting file. If you do want a visual aid, though, I've got a rough drawing here:</p>
<p><img src="https://github.com/Fidget-Cube/write-ups/tree/main/2023/DEFCON_CTF_Qualifiers/OMGzip/magic.jpg" alt="magic function"/></p>
<p>So, that's it! The encode() method transforms each byte into an array of bits, which are combined and converted into bytes and returned. Once you understand the program, you can write your decompression algorithm.</p>
<p>In my solution, I copied over the entire Family and Deflater classes into my decompression script, since their functions can still be used for decompression (I did change some of the names to make more sense though). I added a new function, decode(), that starts by converting the entire file into a bit array, since that's the last step performed by encode(). I then wrote another new function, _tribute(), which, in combination with decode(), iterates over the entire bit array and recovers each byte one by one. Since _travesty() traverses the binary tree from bottom to top, _tribute() instead traverses it from top to bottom. The path it follows is determined by the bit array; if a 1 is encountered, it goes to the left child, if a 0, the right. Once it finds a node with no children, it can simply read the data inside the node to get the original byte used to encode the bits. If the decompression is done in order, starting from the beginning of the file, I can also run _magic() (renamed _scrambler) on the node that was found to update the binary tree the same way it was updated during compression. This process of using input bits to traverse the tree, then running _magic() each time we complete a byte, is looped over until we run out of input bits.</p>
<p>In total, to decompress the file data.tar.omgzip, you need to perform 3 steps. First, remove the OMGZIP header. Second, use your custom decode() function within the Deflater class to decode the bits of the file into their original bytes. Third and finally, use your reversed compress() function to expand the repeated bits of the file. And that's it! Write the bytes to data.tar, un-tar the archive, and get the flag from the "flag" file!</p>
<p>(Alternatively, just run decompressor.py on data.tar.omgzip. But that's just using my solution, and that's no fun right?)</p>
<p>Addendum:<br/>
The implementation I wrote in Python is really slow, taking around 15-20 minutes to decompress the whole file. A faster implementation could be written in a faster language, like C++ or Rust. My friend [Sam](reference needed) helped a lot with the completion of this challenge and wrote his own implementation of the algorithm in Rust. You can find his solution [here](reference needed).</p>
<!-- Categories:Reverse Engineering -->
        </content>
        <summary type="html">
            <p>Just this past weekend, <a href="https://www.meetup.com/pacifichackers/">Pacific Hackers Association</a> and <a href="https://www.meetup.com/hackmiami/">HackMiami</a> joined forces to compete in the <a href="https://quals.2023.nautilus.institute/index.html">DEFCON CTF Qualifiers</a>. The top 12 teams from this CTF go on to play the most competitive and challenging CTF in the world at DEFCON 31. Though, to be fair, our team wasn't of that caliber. We were able to solve 2 challenges (not counting the sanity checks) and finished in 92nd place, which is a great success by my standards! I was able to solve this one with the help of my friend Sam. It's a reverse engineering challenge, but with Python code instead of x86 assembly, which makes things much nicer.</p>
        </summary>
        <author>
            <name>Max vonBlankenburg</name>
        </author>
        <category>Reverse Engineering</category>
    </entry>
    <entry>
        <title>Provably Secure 2</title>
        <link href="https://fidgetcube.dev/index.php?page=post&amp;name=provably_secure_2" rel="alternate" type="text/html"/>
        <published>2023-02-08T00:00:00Z</published>
        <updated>2023-02-08T00:00:00Z</updated>
        <id>https://fidgetcube.dev/index.php?page=post&amp;name=provably_secure_2</id>
        <content type="html">
            <h1>Provably Secure 2</h1>
<h2>Now with less cheese! Still pretty simple though. <code>nc mc.ax 31497</code></h2>
<blockquote>
<p>category: Crypto<br/>
author: jyu<br/>
solves: 155<br/>
points: 117<br/>
challenge file(s): <a href="https://github.com/Fidget-Cube/write-ups/tree/main/2023/DiceCTF/Provably-Secure-2/server.py">server.py</a><br/>
solution file(s): <a href="https://github.com/Fidget-Cube/write-ups/tree/main/2023/DiceCTF/Provably-Secure-2/client.py">client.py</a>  </p>
</blockquote>
<p>This server is basically a simulation of the IND-CCA2 game testing a custom cryptographic system. The game is described in detail here <a href="https://en.wikipedia.org/wiki/Ciphertext_indistinguishability">https://en.wikipedia.org/wiki/Ciphertext_indistinguishability</a>.  </p>
<p>The server makes 128 passes, generating a random bit (0 or 1) each pass. Our goal is to call a "Solve" function, and correctly "guess" the bit 128 times, at which point a flag is printed. In addition, the server also provides "Query Encryption" and "Query Decryption" functions.  </p>
<p>The "Query Encryption" Function asks for 2 messages to encrypt. If the random bit is 0 it encrypts the first one and returns the ciphertext, and if the bit is 1 it encrypts the second. Unfortunately, we are unable to predict this bit using encryption alone, since this cryptosystem is IND-CPA secured. That is, when the cryptosystem is given the same message multiple times, it will encrypt it differently every time. We cannot simply work backwards by comparing ciphertext with their respective plaintext.  </p>
<p>The "Query Decryption" Function takes an encrypted message and decrypts it. Unlike in Provably Secure (original), we can't simply query decryption on ciphertext we just encrypted, which would immediately tell us what message produced the ciphertext. The function checks our input to make sure it doesn't match past encryption queries.  </p>
<p>So we can't cheese this game. We have to prove that this cryptosystem is not IND-CCA2 secured somehow.  </p>
<p>The encryption process uses 2 RSA public/private keys (r0, r1), and goes something like this:  </p>
<p><code>ciphertext = r0-public-key(random_data) + r1-public-key(plaintext ⊕ random_data)</code>  </p>
<p>And the decryption process (with ciphertext split in half):  </p>
<p><code>plaintext = r0-private-key(ciphertext0) ⊕ r1-private-key(ciphertext1)</code>  </p>
<p>This operation works because of properties of the XOR (⊕) operation. Namely, if a ⊕ b = c, then c ⊕ b = a and c ⊕ a = b. We can actually use this property to our advantage.  </p>
<p>Let's make 2 encryption queries. For the first, we'll make m0 00000000000000000000000000000000 for simplicity, and m1 ffffffffffffffffffffffffffffffff for fun.  </p>
<p><code>m0 (16 byte hexstring): 00000000000000000000000000000000</code>
<code>m1 (16 byte hexstring): ffffffffffffffffffffffffffffffff</code>
<code>298c7e2c...</code></p>
<p>For the second, we'll make both messages the same (also 00000000000000000000000000000000 for simplicity).  </p>
<p><code>m0 (16 byte hexstring): 00000000000000000000000000000000</code>
<code>m1 (16 byte hexstring): 00000000000000000000000000000000</code>
<code>7f5b2a85...</code></p>
<p>From what we know about the encryption process, both of these outputs are a combination of 2 ciphertexts, I'll name them ct0_a and ct1_a from our first query, and ct0_b and ct1_b from our second. For the rest of this proof, "a" will denote data from our first encryption query, and "b" will denote the second.  </p>
<p>Since these ciphertexts are encrypted and decrypted separately, we can mix them around. What if we were to pair ct0_a with ct1_b, and ct0_b with ct1_a?  </p>
<p><code>r0-private-key(ct0_a) ⊕ r1-private-key(ct1_b) = (random_data_a) ⊕ (plaintext_b ⊕ random_data_b)</code>  </p>
<p><code>r0-private-key(ct0_b) ⊕ r1-private-key(ct1_a) = (random_data_b) ⊕ (plaintext_a ⊕ random data_a)</code>  </p>
<p>When we made the second query before, we made sure that plaintext_b was 00000000000000000000000000000000 for a reason. Any bit XOR-ed with 0 is itself, the identity property. For this reason, we can remove plaintext_b from the expression, as it does not affect the final XOR product.  </p>
<p><code>random_data_a ⊕ random_data_b</code>  </p>
<p><code>random_data_b ⊕ plaintext_a ⊕ random data_a</code>  </p>
<p>Using the other property of XOR we know about, we can simply XOR these two values together, and the result will be plaintext_a.  </p>
<p><code>random_data_a ⊕ random_data_b ⊕ random_data_b ⊕ plaintext_a ⊕ random data_a = plaintext_a</code>  </p>
<p>And we've successfully recovered plaintext from a ciphertext message! If plaintext_a is 00000000000000000000000000000000, we know m0 was used to make the ciphertext, meaning the random bit is 0. If plaintext_a is ffffffffffffffffffffffffffffffff, the inverse is true, and the random bit is 1.  </p>
<!-- Categories:Cryptography -->
        </content>
        <summary type="html">
            <p>This server is basically a simulation of the IND-CCA2 game testing a custom cryptographic system. The game is described in detail here <a href="https://en.wikipedia.org/wiki/Ciphertext_indistinguishability">https://en.wikipedia.org/wiki/Ciphertext_indistinguishability</a>.  </p>
        </summary>
        <author>
            <name>Max vonBlankenburg</name>
        </author>
        <category>Cryptography</category>
    </entry>
</feed>
