<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Madalina Stoicov</title><link>https://www.mstoi.co/</link><description>Recent content on Madalina Stoicov</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><copyright>Madalina Stoicov (CC by 4.0)</copyright><lastBuildDate>Mon, 30 Mar 2026 16:59:19 -0400</lastBuildDate><atom:link href="https://www.mstoi.co/index.xml" rel="self" type="application/rss+xml"/><item><title>UMass CTF 2026: rev/Batcave Bitflips</title><link>https://www.mstoi.co/blog/umass26-batcave/</link><pubDate>Mon, 13 Apr 2026 14:09:28 -0400</pubDate><guid>https://www.mstoi.co/blog/umass26-batcave/</guid><description>&lt;h1 id="umass-ctf-2026-revbatcave-bitflips"&gt;UMASS CTF 2026: rev/Batcave Bitflips&lt;/h1&gt;
&lt;h2 id="context"&gt;Context&lt;/h2&gt;
&lt;p&gt;The challenge files include one binary named &lt;code&gt;batcave_license_checker&lt;/code&gt; and the following description:&lt;/p&gt;





&lt;pre tabindex="0"&gt;&lt;code&gt;Batman&amp;#39;s new state-of-the-art AI agent has deleted all of the source code to the Batcave license
verification program! There&amp;#39;s an old debug version lying around, but that thing has been hit by
more cosmic rays than Superman!&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="basic-static-and-dynamic-analysis"&gt;Basic Static and Dynamic Analysis&lt;/h2&gt;
&lt;p&gt;As with any reverse engineering challenge, we begin with basic static and dynamic analysis.&lt;/p&gt;</description><content:encoded><![CDATA[<h1 id="umass-ctf-2026-revbatcave-bitflips">UMASS CTF 2026: rev/Batcave Bitflips</h1>
<h2 id="context">Context</h2>
<p>The challenge files include one binary named <code>batcave_license_checker</code> and the following description:</p>





<pre tabindex="0"><code>Batman&#39;s new state-of-the-art AI agent has deleted all of the source code to the Batcave license
verification program! There&#39;s an old debug version lying around, but that thing has been hit by
more cosmic rays than Superman!</code></pre><h2 id="basic-static-and-dynamic-analysis">Basic Static and Dynamic Analysis</h2>
<p>As with any reverse engineering challenge, we begin with basic static and dynamic analysis.</p>
<p>I ran <code>file</code>, saw this was an ELF x86 binary, and spun up my Linux server to run it. <code>strings</code> showed a few interesting things:</p>





<pre tabindex="0"><code>0123456789abcdef
=================================================================================
_-_-_-_-_-_-_-_-_-_-_- BATCAVE LICENSE VERIFICATION (Beta) _-_-_-_-_-_-_-_-_-_-_-
=================================================================================
ENTER LICENSE KEY:
COMPUTING...
HASHED KEY: %s
VERIFYING...
INVALID LICENSE - PLEASE CONTACT ALFRED
LICENSE GOOD - DECRYPTING BAT DATA...
FLAG: %s
!_batman-robin-alfred_((67||67));Tu</code></pre><p>Additionally, running the file confirmed that this challenge was a take on a classic &ldquo;license-checking&rdquo; RE challenge:</p>





<pre tabindex="0"><code>mmstoic@clac:~/personal/tmp/umass/batcave$ ./batcave_license_checker
=================================================================================
_-_-_-_-_-_-_-_-_-_-_- BATCAVE LICENSE VERIFICATION (Beta) _-_-_-_-_-_-_-_-_-_-_-
=================================================================================

ENTER LICENSE KEY: 12345
COMPUTING...
HASHED KEY: 0222db82e8613918515811e0e1aa2b900819339062a1e23069c1ab303b521b7a
VERIFYING...
INVALID LICENSE - PLEASE CONTACT ALFRED</code></pre><p>Past &ldquo;license-checking&rdquo; challenges I&rsquo;ve done have always involved diving into some kind of crypto, so I kept that in mind as I put the file into IDA.</p>
<h2 id="advanced-static-analysis-in-ida">Advanced Static Analysis in IDA</h2>
<p>The main function of the program takes in the key from the user, calls a hash function, converts the result of the hash function to hex so the hash can be printed, calls a verify function, and then prints the result of that verification.</p>
<p>One small thing I noticed was the size passed into <code>fgets</code> to get the user&rsquo;s license key data: <code>0x21</code>, or 32 bytes and a NULL terminator. I wondered if one of the interesting strings I saw earlier (<code>!_batman-robin-alfred_((67||67))</code>) was the correct size. Indeed, the string is 32 bytes (32 letters), but it didn&rsquo;t pass the license check.</p>





<pre tabindex="0"><code>mmstoic@clac:~/personal/tmp/umass/batcave$ ./batcave_license_checker
=================================================================================
_-_-_-_-_-_-_-_-_-_-_- BATCAVE LICENSE VERIFICATION (Beta) _-_-_-_-_-_-_-_-_-_-_-
=================================================================================

ENTER LICENSE KEY: !_batman-robin-alfred_((67||67))
COMPUTING...
HASHED KEY: fa189b817a02bbd3f1a88bf1c942211b80fa61fb39aa30708960d9020b71e0e3
VERIFYING...
INVALID LICENSE - PLEASE CONTACT ALFRED</code></pre><p>Initially, I thought this string was just a red herring, so I ignored and continued my analysis.</p>
<h3 id="hash-function">hash() function</h3>
<p>This is the meat of the challenge. Careful static analysis revealed that the function takes the following steps:</p>
<ul>
<li>Use an <code>expand_state()</code> function to expand the 32-byte input to 64 bytes</li>
<li>In a loop from 0 to 0xBEEEEE:
<ul>
<li>Call a <code>substitute()</code> function which replaces every byte in the array with its mapping in a given SBOX</li>
<li>Call a <code>mix()</code> function which diffuses the bytes in the array</li>
<li>Call a <code>rotate()</code> function which rotates the bytes in the array</li>
</ul>
</li>
<li>Collapse the array from 64 bytes back to 32 bytes in <code>derive_final()</code></li>
<li>In a <code>verify()</code> function, do a memcmp between the array and a given array of bytes</li>
<li>If the verification passes, then the flag is decrypted in <code>decrypt_flag()</code></li>
</ul>
<p>During my analysis of these various functions I found 2 interesting bugs.</p>
<h2 id="bugs--vulnerabilities">Bugs &amp; Vulnerabilities</h2>
<h3 id="rotate-function">rotate() function</h3>
<p>This is the core of the rotate function:</p>





<pre tabindex="0"><code>.text:000000000000125E
.text:000000000000125E                         loc_125E:               ; eax has value of counter
.text:000000000000125E 8B 45 FC                mov     eax, [rbp+counter]
.text:0000000000001261 48 63 D0                movsxd  rdx, eax        ; extend the value of counter
.text:0000000000001264 48 8B 45 E8             mov     rax, [rbp+input_bytes_extended] ; pointer to extended input array
.text:0000000000001268 48 01 D0                add     rax, rdx        ; move the pointer counter# in
.text:000000000000126B 0F B6 00                movzx   eax, byte ptr [rax] ; get the value at the array there
.text:000000000000126E 88 45 FB                mov     [rbp+byte_version_of_array_index], al ; get byte version of what&#39;s in the array at counter# index
.text:0000000000001271 0F B6 45 FB             movzx   eax, [rbp+byte_version_of_array_index] ; extend that byte value
.text:0000000000001275 8D 14 C5 00 00 00 00    lea     edx, ds:0[rax*8] ; edx = rax * 8 = rax &lt;&lt; 3
.text:000000000000127C 0F B6 45 FB             movzx   eax, [rbp+byte_version_of_array_index] ; get that byte again, extended
.text:0000000000001280 C0 E8 06                shr     al, 6           ; shift that byte right by 6
.text:0000000000001283 89 D1                   mov     ecx, edx
.text:0000000000001285 09 C1                   or      ecx, eax        ; OR the bytes together: the one shifted left by 3 and the one shifted right by 6
.text:0000000000001287 8B 45 FC                mov     eax, [rbp+counter] ; move the value of the counter into eax
.text:000000000000128A 48 63 D0                movsxd  rdx, eax        ; extend the counter value into rdx
.text:000000000000128D 48 8B 45 E8             mov     rax, [rbp+input_bytes_extended]
.text:0000000000001291 48 01 D0                add     rax, rdx        ; move pointer counter# into the array
.text:0000000000001294 89 CA                   mov     edx, ecx        ; move the OR&#39;d bytes from earlier into edx
.text:0000000000001296 88 10                   mov     [rax], dl       ; replace the value in the array with the OR&#39;d bytes, but only take the last byte
.text:0000000000001298 83 45 FC 01             add     [rbp+counter], 1 ; increase the counter</code></pre><p>The overall formula is essentially <code>extended_array[i] = (extended_array[i] &lt;&lt; 3) OR (extended_array[i] &gt;&gt; 6)</code>. This may cause us to lose some information. Imagine we have a byte 0b11100001. Shifting the number 3 times to the left results in 0b00001000, and shifting the number 6 times to the right results in 0b00000011. OR&rsquo;d together, we get 0b00001011, and we lose 1 bit of information. Since rotations are simply supposed to redistribute information and not lose it, this rotation is buggy. Instead, we should have two numbers that add up to 8 (because there are 8 bits in a byte), like 2 &amp; 6, or 3 &amp; 5.</p>
<h3 id="sbox">SBOX</h3>
<p>I ran some tests against the provided SBOX to see if anything was out of order there.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">sbox</span> <span class="o">=</span> <span class="p">[</span><span class="mh">0xCF</span><span class="p">,</span> <span class="mh">0x6E</span><span class="p">,</span> <span class="mh">0xFE</span><span class="p">,</span> <span class="mh">0x35</span><span class="p">,</span> <span class="mh">0x46</span><span class="p">,</span> <span class="mh">0x1A</span><span class="p">,</span> <span class="mh">0xAD</span><span class="p">,</span> <span class="mh">0x58</span><span class="p">,</span> <span class="mh">0x78</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x54</span><span class="p">,</span> <span class="mh">0x84</span><span class="p">,</span> <span class="mh">0x94</span><span class="p">,</span> <span class="mh">0xFF</span><span class="p">,</span> <span class="mh">0x70</span><span class="p">,</span> <span class="mh">0x30</span><span class="p">,</span> <span class="mh">0x07</span><span class="p">,</span> <span class="mh">0x45</span><span class="p">,</span> <span class="mh">0x34</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="mh">0xCD</span><span class="p">,</span> <span class="mh">0x40</span><span class="p">,</span> <span class="mh">0xF6</span><span class="p">,</span> <span class="mh">0x5B</span><span class="p">,</span> <span class="mh">0x43</span><span class="p">,</span> <span class="mh">0xB4</span><span class="p">,</span> <span class="mh">0x79</span><span class="p">,</span> <span class="mh">0x72</span><span class="p">,</span> <span class="mh">0xA2</span><span class="p">,</span> <span class="mh">0x1B</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="mh">0xB2</span><span class="p">,</span> <span class="mh">0x8E</span><span class="p">,</span> <span class="mh">0xA0</span><span class="p">,</span> <span class="mh">0x6D</span><span class="p">,</span> <span class="mh">0x3C</span><span class="p">,</span> <span class="mh">0x03</span><span class="p">,</span> <span class="mh">0xEE</span><span class="p">,</span> <span class="mh">0x47</span><span class="p">,</span> <span class="mh">0xDF</span><span class="p">,</span> <span class="mh">0x3D</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="mh">0x24</span><span class="p">,</span> <span class="mh">0xB8</span><span class="p">,</span> <span class="mh">0xD4</span><span class="p">,</span> <span class="mh">0xD3</span><span class="p">,</span> <span class="mh">0xD6</span><span class="p">,</span> <span class="mh">0xC0</span><span class="p">,</span> <span class="mh">0xBC</span><span class="p">,</span> <span class="mh">0xE1</span><span class="p">,</span> <span class="mh">0x38</span><span class="p">,</span> <span class="mh">0xCB</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="mh">0xA3</span><span class="p">,</span> <span class="mh">0x9C</span><span class="p">,</span> <span class="mh">0xFC</span><span class="p">,</span> <span class="mh">0xE0</span><span class="p">,</span> <span class="mh">0xBD</span><span class="p">,</span> <span class="mh">0xF2</span><span class="p">,</span> <span class="mh">0x56</span><span class="p">,</span> <span class="mh">0xDB</span><span class="p">,</span> <span class="mh">0x2D</span><span class="p">,</span> <span class="mh">0xA7</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="mh">0x37</span><span class="p">,</span> <span class="mh">0x92</span><span class="p">,</span> <span class="mh">0xE6</span><span class="p">,</span> <span class="mh">0xC4</span><span class="p">,</span> <span class="mh">0x91</span><span class="p">,</span> <span class="mh">0x4F</span><span class="p">,</span> <span class="mh">0x4E</span><span class="p">,</span> <span class="mh">0x67</span><span class="p">,</span> <span class="mh">0x39</span><span class="p">,</span> <span class="mh">0xC3</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="mh">0x83</span><span class="p">,</span> <span class="mh">0x87</span><span class="p">,</span> <span class="mh">0x93</span><span class="p">,</span> <span class="mh">0x25</span><span class="p">,</span> <span class="mh">0x27</span><span class="p">,</span> <span class="mh">0x81</span><span class="p">,</span> <span class="mh">0x42</span><span class="p">,</span> <span class="mh">0xD2</span><span class="p">,</span> <span class="mh">0x89</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="mh">0xF4</span><span class="p">,</span> <span class="mh">0xE5</span><span class="p">,</span> <span class="mh">0x08</span><span class="p">,</span> <span class="mh">0xD8</span><span class="p">,</span> <span class="mh">0x2B</span><span class="p">,</span> <span class="mh">0x5A</span><span class="p">,</span> <span class="mh">0x9A</span><span class="p">,</span> <span class="mh">0x26</span><span class="p">,</span> <span class="mh">0x0B</span><span class="p">,</span> <span class="mh">0x5F</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="mh">0xF5</span><span class="p">,</span> <span class="mh">0x64</span><span class="p">,</span> <span class="mh">0x43</span><span class="p">,</span> <span class="mh">0xA1</span><span class="p">,</span> <span class="mh">0xF1</span><span class="p">,</span> <span class="mh">0xB5</span><span class="p">,</span> <span class="mh">0xE7</span><span class="p">,</span> <span class="mh">0x8D</span><span class="p">,</span> <span class="mh">0x9F</span><span class="p">,</span> <span class="mh">0x98</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="mh">0xB7</span><span class="p">,</span> <span class="mh">0xF0</span><span class="p">,</span> <span class="mh">0x13</span><span class="p">,</span> <span class="mh">0x2C</span><span class="p">,</span> <span class="mh">0xB0</span><span class="p">,</span> <span class="mh">0x97</span><span class="p">,</span> <span class="mh">0x14</span><span class="p">,</span> <span class="mh">0x7E</span><span class="p">,</span> <span class="mh">0x19</span><span class="p">,</span> <span class="mh">0x18</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="mh">0x8F</span><span class="p">,</span> <span class="mh">0xB9</span><span class="p">,</span> <span class="mh">0x23</span><span class="p">,</span> <span class="mh">0xDD</span><span class="p">,</span> <span class="mh">0x77</span><span class="p">,</span> <span class="mh">0x52</span><span class="p">,</span> <span class="mh">0x05</span><span class="p">,</span> <span class="mh">0x09</span><span class="p">,</span> <span class="mh">0x15</span><span class="p">,</span> <span class="mh">0xEF</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">  <span class="mh">0x88</span><span class="p">,</span> <span class="mh">0xEA</span><span class="p">,</span> <span class="mh">0xBF</span><span class="p">,</span> <span class="mh">0x8C</span><span class="p">,</span> <span class="mh">0x11</span><span class="p">,</span> <span class="mh">0x76</span><span class="p">,</span> <span class="mh">0x86</span><span class="p">,</span> <span class="mh">0x60</span><span class="p">,</span> <span class="mh">0x9B</span><span class="p">,</span> <span class="mh">0xBA</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="mh">0x55</span><span class="p">,</span> <span class="mh">0x95</span><span class="p">,</span> <span class="mh">0xB3</span><span class="p">,</span> <span class="mh">0x02</span><span class="p">,</span> <span class="mh">0xFA</span><span class="p">,</span> <span class="mh">0xDC</span><span class="p">,</span> <span class="mh">0x1C</span><span class="p">,</span> <span class="mh">0x49</span><span class="p">,</span> <span class="mh">0x21</span><span class="p">,</span> <span class="mh">0x59</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="mh">0x6F</span><span class="p">,</span> <span class="mh">0xA4</span><span class="p">,</span> <span class="mh">0x01</span><span class="p">,</span> <span class="mh">0x06</span><span class="p">,</span> <span class="mh">0x2A</span><span class="p">,</span> <span class="mh">0x0E</span><span class="p">,</span> <span class="mh">0xA5</span><span class="p">,</span> <span class="mh">0x16</span><span class="p">,</span> <span class="mh">0xE9</span><span class="p">,</span> <span class="mh">0xB6</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="mh">0x5E</span><span class="p">,</span> <span class="mh">0xE2</span><span class="p">,</span> <span class="mh">0x8B</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0xCA</span><span class="p">,</span> <span class="mh">0x57</span><span class="p">,</span> <span class="mh">0x90</span><span class="p">,</span> <span class="mh">0x0F</span><span class="p">,</span> <span class="mh">0x32</span><span class="p">,</span> <span class="mh">0x2E</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="mh">0x4C</span><span class="p">,</span> <span class="mh">0x1E</span><span class="p">,</span> <span class="mh">0x62</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x1D</span><span class="p">,</span> <span class="mh">0xA6</span><span class="p">,</span> <span class="mh">0xC5</span><span class="p">,</span> <span class="mh">0xAA</span><span class="p">,</span> <span class="mh">0xC2</span><span class="p">,</span> <span class="mh">0x41</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">  <span class="mh">0x17</span><span class="p">,</span> <span class="mh">0x69</span><span class="p">,</span> <span class="mh">0xF8</span><span class="p">,</span> <span class="mh">0x3A</span><span class="p">,</span> <span class="mh">0xC9</span><span class="p">,</span> <span class="mh">0x3B</span><span class="p">,</span> <span class="mh">0xEB</span><span class="p">,</span> <span class="mh">0x29</span><span class="p">,</span> <span class="mh">0x6C</span><span class="p">,</span> <span class="mh">0xDE</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="mh">0x10</span><span class="p">,</span> <span class="mh">0x85</span><span class="p">,</span> <span class="mh">0xC8</span><span class="p">,</span> <span class="mh">0xC1</span><span class="p">,</span> <span class="mh">0x99</span><span class="p">,</span> <span class="mh">0x36</span><span class="p">,</span> <span class="mh">0x1F</span><span class="p">,</span> <span class="mh">0x63</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x3E</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">  <span class="mh">0x4D</span><span class="p">,</span> <span class="mh">0x5D</span><span class="p">,</span> <span class="mh">0xD1</span><span class="p">,</span> <span class="mh">0x9E</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0xEC</span><span class="p">,</span> <span class="mh">0xBE</span><span class="p">,</span> <span class="mh">0xCE</span><span class="p">,</span> <span class="mh">0x61</span><span class="p">,</span> <span class="mh">0xB1</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">  <span class="mh">0x0D</span><span class="p">,</span> <span class="mh">0xA9</span><span class="p">,</span> <span class="mh">0x4A</span><span class="p">,</span> <span class="mh">0x96</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0x9D</span><span class="p">,</span> <span class="mh">0x22</span><span class="p">,</span> <span class="mh">0xE4</span><span class="p">,</span> <span class="mh">0xAC</span><span class="p">,</span> <span class="mh">0x7C</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="mh">0xE3</span><span class="p">,</span> <span class="mh">0x71</span><span class="p">,</span> <span class="mh">0xE8</span><span class="p">,</span> <span class="mh">0x7A</span><span class="p">,</span> <span class="mh">0xFD</span><span class="p">,</span> <span class="mh">0xF7</span><span class="p">,</span> <span class="mh">0x2F</span><span class="p">,</span> <span class="mh">0xAE</span><span class="p">,</span> <span class="mh">0xC6</span><span class="p">,</span> <span class="mh">0x8A</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">  <span class="mh">0xF3</span><span class="p">,</span> <span class="mh">0x33</span><span class="p">,</span> <span class="mh">0xC7</span><span class="p">,</span> <span class="mh">0x0C</span><span class="p">,</span> <span class="mh">0x82</span><span class="p">,</span> <span class="mh">0x53</span><span class="p">,</span> <span class="mh">0xFB</span><span class="p">,</span> <span class="mh">0xDA</span><span class="p">,</span> <span class="mh">0x51</span><span class="p">,</span> <span class="mh">0x7B</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">  <span class="mh">0x04</span><span class="p">,</span> <span class="mh">0xBB</span><span class="p">,</span> <span class="mh">0x7F</span><span class="p">,</span> <span class="mh">0x50</span><span class="p">,</span> <span class="mh">0xA8</span><span class="p">,</span> <span class="mh">0x6A</span><span class="p">,</span> <span class="mh">0xAF</span><span class="p">,</span> <span class="mh">0x6B</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x7D</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">  <span class="mh">0x28</span><span class="p">,</span> <span class="mh">0xF9</span><span class="p">,</span> <span class="mh">0x3F</span><span class="p">,</span> <span class="mh">0x12</span><span class="p">,</span> <span class="mh">0xD5</span><span class="p">,</span> <span class="mh">0x0A</span><span class="p">,</span> <span class="mh">0x66</span><span class="p">,</span> <span class="mh">0x80</span><span class="p">,</span> <span class="mh">0xD0</span><span class="p">,</span> <span class="mh">0x4B</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">  <span class="mh">0x5C</span><span class="p">,</span> <span class="mh">0xD7</span><span class="p">,</span> <span class="mh">0xD9</span><span class="p">,</span> <span class="mh">0xAB</span><span class="p">,</span> <span class="mh">0xCC</span><span class="p">,</span> <span class="mh">0xED</span><span class="p">]</span></span></span></code></pre></div><p>Recall that SBOX&rsquo;s are meant to obscure the connection between the input and output by mapping every possible byte (from 0x00 to 0xFF) to some other byte. Thus, there are a few properties SBOX&rsquo;s should hold:</p>
<ul>
<li>No output byte should be reached by two different input bytes (ex: 0x00 and 0x01 cannot both map to 0xAB)</li>
<li>Every possible input byte should exist somewhere in the SBOX. If you&rsquo;re covering 0x00 to 0xFF as inputs, each one of those values should be present somewhere in the SBOX. This also means there can be no duplicate values in the SBOX.</li>
</ul>
<p>I ran a simple Python script (<a href="https://github.com/mmstoic/writeups-mmstoic/blob/main/umassctf/batcave/sbox_checker.py">sbox_checker.py</a>) to test some of these features, and found that the value 0x44 was missing from the SBOX. I noted that 0x44 is ASCII 68, so I checked the values for 0x43 (ASCII 67) and 0x45 (ASCII 69) (using my GenZ/brainrot/OSINT thinking here). Indeed, I found that there were two 0x43&rsquo;s (ASCII 67&rsquo;s). This double 67 aligns with the key I found in strings earlier (<code>!_batman-robin-alfred_((67||67))</code>) and it made me think that maybe this key is the correct one after all.</p>
<h2 id="patching--solution">Patching &amp; Solution</h2>
<p>Putting it all together, I realized that maybe the challenge isn&rsquo;t necessarily about getting the key, but instead about patching the file so that the given key we found in strings (<code>!_batman-robin-alfred_((67||67))</code>) works. Since each of the bugs have two places that could be patched, we have 4 possible altered binaries:</p>
<ol>
<li>v1: Change first 0x43 to 0x44 &amp; change shift 3 to shift 2</li>
<li>v2: Change first 0x43 to 0x44 &amp; change shift 6 to shift 5</li>
<li>v3: Change second 0x43 to 0x44 &amp; change shift 3 to shift 2</li>
<li>v4: Change second 0x43 to 0x44 &amp; change shift 6 to shift 5</li>
</ol>
<h3 id="applying-the-patches">Applying the patches</h3>
<p>I did all my patching in HxD, which comes pre-loaded on FlareVM! Some of these lines of assembly were a bit complex, so patching them required a bit of extra research.</p>
<h3 id="patching-the-shift-left-from-3-to-2">Patching the shift left from 3 to 2</h3>
<p>Firstly, the shift left that happens in <code>rotate()</code> at address 0x1275 isn&rsquo;t just a <code>shl</code> instruction:</p>
<p><code>.text:0000000000001275 8D 14 C5 00 00 00 00    lea     edx, ds:0[rax*8] ; edx = rax * 8 = rax &lt;&lt; 3</code></p>
<p><code>ds:</code> refers to data segment, a concept that was needed for 32-bit systems to calculate addresses. In 64-bit systems, <code>ds</code> has a base of 0, so <code>ds:0</code> means an offset of 0 from the base, which is 0. Recall also that multiplying by 8 is the same as shifting left by 3. Thus, <code>lea     edx, ds:0[rax*8]</code> collapses into shifting the 8-byte value at <code>rax</code> left by 3 and storing that in <code>edx</code>. Later in <code>rotate()</code>, we isolate only the last byte of the value.</p>
<p><code>8D 14 C5 00 00 00 00</code> represents this whole instruction. 8D is the opcode for lea, 14 represents what register we&rsquo;re moving our value to and if we have an SIB byte to consider, and C5 is the SIB byte. 0xC5 = 0b11000101 (for legibility: 0b11 000 101), where 000 represents the register we&rsquo;re taking from (<code>rax</code>), and 101 represents our base (<code>rbp</code>). 11 represents the scale, where:</p>
<ul>
<li>00 = 1</li>
<li>01 = 2</li>
<li>10 = 4</li>
<li>11 = 8</li>
</ul>
<p>To change our shifting from 3 to 2, we need to change the scale from 8 to 4. So, instead of C5, we get 0b10000101 = 0x85. Basically, to patch this shift left from &laquo;3 to &laquo;2, we change the bytes for the instruction to <code>8D 14 85 00 00 00 00</code>.</p>
<p>See <a href="https://wiki.osdev.org/X86-64_Instruction_Encoding">this link</a> for more information on instruction encoding!</p>
<h4 id="patching-the-shift-right-from-6-to-5">Patching the shift right from 6 to 5</h4>
<p>Changing the amount we shift right is a lot easier, since the <code>shr</code> is directly used:</p>
<p><code>.text:0000000000001280 C0 E8 06                shr     al, 6           ; shift that byte right by 6</code></p>
<p>So, patching this is just involves changing 06 to 05.</p>
<h4 id="patching-the-extra-67s-in-the-sbox">Patching the extra 67&rsquo;s in the SBOX</h4>
<p>When it comes to the extra 67&rsquo;s in the SBOX, we can simply change either one of them from 0x43 to 0x44. The first 0x43 is at <code>.data:0000000000004098</code> and the second one is at <code>.data:00000000000040DC</code>.</p>
<h4 id="using-the-patched-files--one-last-patch">Using the patched files &amp; one last patch</h4>
<p>Now that we have 4 patched files, we can try each of them to see which one allows us to pass the validation check. Version 2, which patched the first 0x43 to 0x44 and patched the shift right 6 to shift right 5, passed the verification! However, the flag output wasn&rsquo;t decrypted correctly:</p>





<pre tabindex="0"><code>mmstoic@clac:~/personal/tmp/umass/batcave$ ./batcave_license_checker_v2
=================================================================================
_-_-_-_-_-_-_-_-_-_-_- BATCAVE LICENSE VERIFICATION (Beta) _-_-_-_-_-_-_-_-_-_-_-
=================================================================================

ENTER LICENSE KEY: !_batman-robin-alfred_((67||67))
COMPUTING...
HASHED KEY: 3b54751a2406af05778047c5e483d348cb8730de1a9145ab15c79b2204022bee
VERIFYING...
LICENSE GOOD - DECRYPTING BAT DATA...
FLAG: ]u[w?_w?w????_???&gt;?g?u??&#39;+??n?5F?XxusT???p0E4?@?[D?yr???m&lt;?G?=$???????8ˣ????V?-?7??đONg9Ã??%&#39;?B҉</code></pre><p>I took a look at the decryption function and noticed that the bytes of the encrypted data were being OR&rsquo;d instead of XOR&rsquo;d:</p>
<p><code>.text:00000000000012EC 09 C1                   or      ecx, eax</code></p>
<p>This means that applying the key onto the encrypted data wouldn&rsquo;t actually get us the plain text, since OR&rsquo;ing doesn&rsquo;t have the same reversal effect as XOR&rsquo;ing. So, I did one last patch to change the opcode of OR (09) to XOR (31) to make a fifth version of the binary.</p>
<p><img src="/blog/umass26-batcave/patching.png" alt="patching is cool"></p>
<p>Finally, this version was successful!</p>





<pre tabindex="0"><code>mmstoic@clac:~/personal/tmp/umass/batcave$ ./batcave_license_checker_v5
=================================================================================
_-_-_-_-_-_-_-_-_-_-_- BATCAVE LICENSE VERIFICATION (Beta) _-_-_-_-_-_-_-_-_-_-_-
=================================================================================

ENTER LICENSE KEY: !_batman-robin-alfred_((67||67))
COMPUTING...
HASHED KEY: 3b54751a2406af05778047c5e483d348cb8730de1a9145ab15c79b2204022bee
VERIFYING...
LICENSE GOOD - DECRYPTING BAT DATA...
FLAG: UMASS{__p4tche5_0n_p4tche$__#}</code></pre><h1 id="sourcescredits">Sources/Credits</h1>
<p>Shoutout to gregt114 for the awesome challenge!</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/ModR/M#SIB_byte">https://en.wikipedia.org/wiki/ModR/M#SIB_byte</a></li>
<li><a href="https://wiki.osdev.org/X86-64_Instruction_Encoding">https://wiki.osdev.org/X86-64_Instruction_Encoding</a></li>
<li><a href="https://en.wikipedia.org/wiki/S-box">https://en.wikipedia.org/wiki/S-box#</a></li>
<li>Special thanks to my FlareVM and free IDA 🙏</li>
</ul>
<p>Written by Madalina Stoicov</p>
]]></content:encoded></item><item><title>About</title><link>https://www.mstoi.co/about/</link><pubDate>Mon, 30 Mar 2026 16:59:19 -0400</pubDate><guid>https://www.mstoi.co/about/</guid><description>&lt;h2 id="education"&gt;Education&lt;/h2&gt;
&lt;p&gt;==&amp;gt; Computer Science @ Barnard College, Columbia University ♔&lt;/p&gt;
&lt;h2 id="work-experience"&gt;Work Experience&lt;/h2&gt;
&lt;p&gt;==&amp;gt; Security Engineer Intern @ Microsoft (Incoming, Summer 2026)&lt;/p&gt;
&lt;p&gt;==&amp;gt; Teaching Assistant for COMS4157 &amp;ldquo;Advanced Systems Programming&amp;rdquo; @ Columbia University w/ Prof. Jae Woo Lee (Spring 2026)&lt;/p&gt;
&lt;p&gt;==&amp;gt; Teaching Assistant for COMS3157 &amp;ldquo;Advanced Programming&amp;rdquo; @ Columbia University w/ Prof. Jae Woo Lee (Fall 2025)&lt;/p&gt;
&lt;p&gt;==&amp;gt; Security Engineer Intern @ Microsoft (Summer 2025)&lt;/p&gt;
&lt;p&gt;==&amp;gt; Student Researcher @ Columbia University (Fall 2024)&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="education">Education</h2>
<p>==&gt; Computer Science @ Barnard College, Columbia University ♔</p>
<h2 id="work-experience">Work Experience</h2>
<p>==&gt; Security Engineer Intern @ Microsoft (Incoming, Summer 2026)</p>
<p>==&gt; Teaching Assistant for COMS4157 &ldquo;Advanced Systems Programming&rdquo; @ Columbia University w/ Prof. Jae Woo Lee (Spring 2026)</p>
<p>==&gt; Teaching Assistant for COMS3157 &ldquo;Advanced Programming&rdquo; @ Columbia University w/ Prof. Jae Woo Lee (Fall 2025)</p>
<p>==&gt; Security Engineer Intern @ Microsoft (Summer 2025)</p>
<p>==&gt; Student Researcher @ Columbia University (Fall 2024)</p>
<h2 id="certificates-and-awards">Certificates and Awards</h2>
<p>==&gt; Google Cybersecurity Certificate (Jul. 2024)</p>
<p>==&gt; 3rd Place @ CSAW Embedded Security Competition (Nov. 2025)</p>
<h2 id="community">Community</h2>
<p>==&gt; Vice President of Columbia University Cybersecurity Club (CU Cyber) (Fall 2025-Spring 2026)</p>
<h2 id="technical-experience--projects">Technical Experience &amp; Projects</h2>
<p>Lots of CTFs and random projects on things I want to know more about&hellip; see my Blog :)</p>
<p>==&gt; See my <a href="https://github.com/mmstoic/writeups-mmstoic">GitHub</a> for all of my CTF writeups</p>
<h2 id="contact-me">Contact Me</h2>
<p>Message me on <a href="https://www.linkedin.com/in/madalina-stoicov/">LinkedIn</a> if you&rsquo;d like to get in touch!</p>
]]></content:encoded></item><item><title>Making My Own Site: a Security Lesson</title><link>https://www.mstoi.co/blog/making_this_site/</link><pubDate>Mon, 30 Mar 2026 16:59:19 -0400</pubDate><guid>https://www.mstoi.co/blog/making_this_site/</guid><description>&lt;p&gt;It&amp;rsquo;s true that anyone can make a website. But, when it&amp;rsquo;s so easy to make one, it&amp;rsquo;s easy to not fully understand how it works, either.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been wanting to make my own site for some time now and finally got around to it. After consulting with some friends and mentors, I decided to use Hugo to create the static site and GitHub Pages to host. Although setting up these components was super easy, there were many intricacies in how everything worked. As a security person, I felt an even greater need to thoroughly research every single setting I was toggling on and ask myself even the most primitive of questions.&lt;/p&gt;</description><content:encoded><![CDATA[<p>It&rsquo;s true that anyone can make a website. But, when it&rsquo;s so easy to make one, it&rsquo;s easy to not fully understand how it works, either.</p>
<p>I&rsquo;ve been wanting to make my own site for some time now and finally got around to it. After consulting with some friends and mentors, I decided to use Hugo to create the static site and GitHub Pages to host. Although setting up these components was super easy, there were many intricacies in how everything worked. As a security person, I felt an even greater need to thoroughly research every single setting I was toggling on and ask myself even the most primitive of questions.</p>
<p>So, here is how I made my site. I&rsquo;ll talk about the logistical stuff, like how to use Hugo, and the security stuff, like how it&rsquo;s super easy to almost include inline styles and open yourself up to an array of XSS attacks.</p>
<h3 id="domain">Domain</h3>
<p>You may have noticed my custom domain: mstoi.co. It was $12 on Namecheap and is close enough to my name, so I thought why not just grab it?</p>
<p>The site is hosted on GitHub Pages (easy and free with GitHub Pro, which I have for free since I&rsquo;m a student) and it was super simple to route everything through Namecheap.</p>
<h3 id="how-does-hugo-work">How does Hugo work?</h3>
<p>Hugo is a static site generator written in Go. It stood out to me as the perfect way to build a site for a few reasons:</p>
<ul>
<li>It&rsquo;s a static site builder, which means each user just gets the pages and there&rsquo;s no extra server-side or database work. This doesn&rsquo;t make the site 100% secure automactially, but greatly reduces the attack space. It&rsquo;s also just easier.</li>
<li>Comes with some templates so I A) don&rsquo;t have to become a graphic designer overnight, and B) can get it all up quicker.</li>
<li>Translates Markdown files into HTML using Markdownify, making it simpler and easier to make posts and changes without manually editing HTML (because who wants to do that?).</li>
</ul>
<p>Hugo expects a certain file layout and structure, with content in the <code>content</code> directory, the directory structure of all of the pages laid out in the <code>hugo.toml</code> configuration file, and a <code>theme/theme-name</code> directory to be able to reference <code>.css</code> files and other theme-specific content. Hugo will generate the HTML files automatically. Running <code>hugo server</code> (or <code>hugo server -D</code> to add in files that are marked as <code>draft = true</code>) will spawn a local instance of the server to test out any changes.</p>
<h3 id="security-considerations">Security Considerations</h3>
<p>One of the things that initially drew me towards static sites is that the attack space is reduced by default. No backend or server-side logic oftentimes means no user input. Additionally, if you can take out JavaScript, that&rsquo;s even better. However, I still had to ask myself: are static sites 100% secure? I went down a rabbit hole to find out.</p>
<p>To start, I chose Bear Cub for my theme. It&rsquo;s simple, gets the job down, and marketed something very important to me: security. Bear Cub is secure in the following ways:</p>
<ul>
<li>Doesn&rsquo;t use JavaScript at all: Although not using JavaScript doesn&rsquo;t make the site XSS-free, the attack space is reduced.</li>
<li>Doesn&rsquo;t take in any user input</li>
<li>Has the option to disallow inline styles</li>
</ul>
<p>Furthermore, I pen tested the site myself. I tried XSS payloads in <code>&lt;img&gt;</code> tags (<code>&lt;img src=x onerror=alert(1)&gt;</code>) and did get result reflected back, but it doesn&rsquo;t get stored. Additionally, path traversal can be done in the URL, but only to get other pages in the site. The site also makes a request to fonts.googleapi.com, but that&rsquo;s just to get the custom font I wanted; it&rsquo;s not a request to my site. In all, since there&rsquo;s no user input on the site, there&rsquo;s nowhere for data to even go.</p>
<p>As I use Hugo and add more to this site, I&rsquo;m sure I&rsquo;ll ask myself even more security questions. As they come up, I&rsquo;ll update this page.</p>
<p>Overall, I&rsquo;ve had a really great time setting up the site and am happy with the result! If you&rsquo;d like to try Hugo yourself, visit <a href="gohugo.io">gohugo.io</a>, and to check out Bear Cub, click <a href="https://themes.gohugo.io/themes/hugo-bearcub/">here</a>.</p>
]]></content:encoded></item><item><title>LA CTF 2026: rev/starless-c</title><link>https://www.mstoi.co/blog/lactf26-starlessc/</link><pubDate>Mon, 16 Feb 2026 14:10:28 -0400</pubDate><guid>https://www.mstoi.co/blog/lactf26-starlessc/</guid><description>&lt;h1 id="la-ctf-2026-revstarless-c"&gt;LA CTF 2026: rev/starless-c&lt;/h1&gt;
&lt;h2 id="context"&gt;Context&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;starless-c&lt;/code&gt; is an excellent reverse engineering challenge written by aplet123 in LA CTF 2026. It requires classic reverse engineering skills in a complex context, combining careful analysis with the familiarity of WASD controls.&lt;/p&gt;
&lt;p&gt;We are given a binary, &lt;code&gt;starless_c&lt;/code&gt;, a netcat server to connect to in order to retrieve the flag, and a short description:&lt;/p&gt;





&lt;pre tabindex="0"&gt;&lt;code&gt;The son of the fortune-teller stands before three doors. A bee. A key. A flag.&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="basic-static-and-dynamic-analysis"&gt;Basic Static and Dynamic Analysis&lt;/h2&gt;
&lt;p&gt;As any reverse engineering process goes, we start by doing a basic static and dynamic analysis on the file. The binary is an ELF 64-bit LSB executable and contains the following strings of interest:&lt;/p&gt;</description><content:encoded><![CDATA[<h1 id="la-ctf-2026-revstarless-c">LA CTF 2026: rev/starless-c</h1>
<h2 id="context">Context</h2>
<p><code>starless-c</code> is an excellent reverse engineering challenge written by aplet123 in LA CTF 2026. It requires classic reverse engineering skills in a complex context, combining careful analysis with the familiarity of WASD controls.</p>
<p>We are given a binary, <code>starless_c</code>, a netcat server to connect to in order to retrieve the flag, and a short description:</p>





<pre tabindex="0"><code>The son of the fortune-teller stands before three doors. A bee. A key. A flag.</code></pre><h2 id="basic-static-and-dynamic-analysis">Basic Static and Dynamic Analysis</h2>
<p>As any reverse engineering process goes, we start by doing a basic static and dynamic analysis on the file. The binary is an ELF 64-bit LSB executable and contains the following strings of interest:</p>





<pre tabindex="0"><code>e flag.
ds to thPH
that leaPH
he path PH
hoose. TPH
you to cPH
ath for PH
ly one pPH
re is onPH
 Now thePH
ges ago.PH
s and paPH
any bytePH
, lost mPH
 is pastPH
ime thatPH
, in a tPH
hs, oncePH
many patPH
re were PH
low. ThePH
h to folPH
heir patPH
ge has tPH
 challenPH
r into aPH
 this faPH
A personP
flag.txtP</code></pre><p>When running the binary, we are given a phrase and then are prompted for input. When trying a random character (that is apparently not correct), we get a type of error message:</p>





<pre tabindex="0"><code>mmstoic@server:~/personal/tmp$ ./starless_c
There is a flag in the binary.
  (The flag is a metaphor but also still a flag.)
  (The binary could rightly be considered a gimmick.)
p
And so the son of the fortune-teller does not find his way to the Starless C. Not yet.</code></pre><p>With some strings to search for, we can begin our advanced static analysis in IDA.</p>
<h2 id="advanced-static-analysis-in-ida">Advanced Static Analysis in IDA</h2>
<p>Immediately we see why our strings looked weird earlier: the binary is using a common obfuscation technique known as stack string obfuscation. Instead of writing the desired string in a <code>char*</code>, for example, the equivalent ASCII values of each letter are pushed onto the stack and get written out instead. This means that we can&rsquo;t just search for some text inside of the binary; we have to use the corresponding ASCII values instead. For example, to find where &ldquo;flag.txt&rdquo; resides in the file, we must instead search for <code>7478742E67616C66</code> or a subset like <code>7478742E</code> (&quot;.txt&quot;) (note the little endian ordering).</p>
<p>Putting this subset into the &ldquo;Search for Sequence of Bytes&rdquo; in IDA leads us to this section of the binary where we see another form of obfuscation taking place. Code is being interpreted as data. By pressing <code>c</code> we can transform the section into code and see specifically where the &ldquo;.txt&rdquo; bytes are.</p>
<p><img src="/blog/lactf26-starlessc/code_as_data1.png" alt="Code as data example"></p>
<p><img src="/blog/lactf26-starlessc/code_as_data1_after.png" alt="Code as data after making into code example"></p>
<p>We see this is the section of the binary that gives us the flag and prints a success message:</p>





<pre tabindex="0"><code>A person this far into a challenge has their path to follow. There were many paths, once, in a time that is past, lost many bytes and pages ago. Now there is only one path for you to choose. The path that leads to the flag.</code></pre><p>Now that we know where our &ldquo;win&rdquo; function is, we can continue analyzing the binary and work our way towards the function.</p>
<p>Starting at the beginning of the binary, the intro message gets printed and a sigaction signal handler gets set on signal 11: SIGSEGV (segmentation fault/violation). We also see the handler for the signal is at <code>LOAD:0000000013370103</code>. The function here simply prints the error message stated above and then the program exits. So, we know that the reason we see this error message is because the program has segfaulted somewhere. Let&rsquo;s keep this in mind.</p>
<p>Looking at the main body of the binary at <code>LOAD:000000006767900C</code>, the logic starts by checking if the input from the user is <code>w</code>, <code>a</code>, <code>s</code>, <code>d</code>, or <code>f</code>. Each one of the WASD options does a similar thing:</p>
<ol>
<li>Check if the contents at a certain function contains the byte <code>0x90</code></li>
<li>If so, overwrite the first 4 bytes of that function&rsquo;s bytes with <code>0088C031</code> and overwrite the first 4 bytes of another function with <code>0x90</code></li>
<li>Jump to somewhere else in the code</li>
</ol>
<p>Take note of the specific bytes being used here. <code>0x90</code> is the NOP byte in x86-64. It executes, but does nothing other than just advance the instruction pointer. <code>0x0088C031</code> is a series of two instructions: <code>xor eax, eax</code> and <code>move [rax], al</code>. This causes a null pointer dereference, which causes a segmentation fault. Thus, we can see here that NOP&rsquo;s and null pointer dereference instructions are being swapped at certain addresses in the binary, essentially allowing us to patch the segfault bytes. This seems to connect with the signal handler for SIGSEGV we saw earlier: the signal handler catches the segfaults at these functions if they are jumped to.</p>
<p>We should also take note of where each WASD option jumps to. Sometimes, it leads to another set of WASD options, sometimes a completely different place futher along in the code, and sometimes goes back to earlier parts of the logic (to earlier WASD options). So, we can see here that the choice of WASD should be chosen carefully to not end up in an infinite loop.</p>
<p>Let&rsquo;s take a closer look at addresses that are inspected for NOP bytes, like the function at <code>LOAD:000000006767A000</code>:</p>
<p><img src="/blog/lactf26-starlessc/example_function.png" alt="Example function"></p>
<p>We see the presence of <code>0088C031</code>, and if turned into code, it is indeed the seg fault-causing instructions.</p>
<p><img src="/blog/lactf26-starlessc/example_function_code.png" alt="Example function code"></p>
<p>We see another example at the function at <code>LOAD:000000006768A000</code>, which starts with NOP bytes instead of the segfault bytes.</p>
<p><img src="/blog/lactf26-starlessc/nop_function.png" alt="nop function"></p>
<p>By further analyzing using cross-references, we can see that this NOP function&rsquo;s bytes are subject to being replaced with segfault-causing bytes during the code logic (see the <code>﻿﻿mov     dword ptr cs:loc_6768A000, 88C031h</code>, for example):</p>
<p><img src="/blog/lactf26-starlessc/xrefs.png" alt="Cross references"></p>
<p>Before we make a conclusion about what&rsquo;s happening here, let&rsquo;s take a look at the <code>f</code> option present among every WASD crossroads. Every time <code>f</code> is pressed, we jump to a function at <code>LOAD:000000006767A000</code> which contains segfault bytes. That function then jumps to a function at <code>LOAD:0000000067682000</code> which also contains seg fault bytes, which then jumps to a function at <code>LOAD:000000006768A000</code> which, as we saw earlier, contains NOP bytes. This function then jumps to <code>LOAD:0000000067691000</code>, which contains more seg fault bytes, and then jumps to <code>LOAD:0000000067692000</code>, which contains more seg fault bytes, and then finally jumps to our flag-printing function we found earlier. So: we have a series of 5 function calls that all contain segfault-causing bytes except for one. This means that if we try to execute the <code>f</code> option before these segfault-causing bytes are patched, we will just get the error message.</p>
<p>After verifying this information by analyzing more WASD and <code>f</code> options and looking at cross-references of various segfault-containing functions, we may draw the following conclusion about what the binary is doing:</p>
<p><code>starless-c</code> uses the user&rsquo;s input of <code>w</code>, <code>a</code>, <code>s</code>, <code>d</code>, and <code>f</code> to navigate through a series of logic checks. Each of these checks may swap 4 bytes present in one function for 4 bytes in another if a NOP is present at a given address. Because these bytes are either NOP bytes or segfault-causing bytes, we have the chance to either patch a segfault or introduce one. At any point, the user can enter <code>f</code> to jump through a series of functions to print the flag, but only if there are no segfault-causing bytes present. Thus, the user has to input the correct amount of <code>w</code>, <code>a</code>, <code>s</code>, <code>d</code>, and <code>f</code> inputs in the correct order to navigate the path to properly patch segfault-causing bytes to print the flag.</p>
<h2 id="solution">Solution</h2>
<p>Just one quick look at the binary will show that are a ton of WASD and <code>f</code> crossroads, so, this challenge can&rsquo;t be solved by going through all of the options manually. During the competition, I asked myself how to proceed with automating the solution. One way is to write a script, but I wasn&rsquo;t quite sure how to manage checking memory locations and traversing the paths of execution. Another way is to use AI, which would be the faster method (especialy in the context of the competition).</p>
<p>I didn&rsquo;t use AI at all during the initial stages of this challenge because I truly thought it would not be able to grasp the complexity of the program. But, I decided to give it a shot. After several failed attempts, I provided Codex (using GPT-5.3-Codex) a long and detailed description of the challenge and how to solve it, as well as my notes on the binary and how I would go about solving the problem if I did it manually. And I couldn&rsquo;t believe it &ndash; it worked! My prompt can be found at the bottom of this blog post. The solution steps are as follows:</p>





<pre tabindex="0"><code>sddddswaasdwaaasdssawwdwddsawasassdddwsddwasaaaawwdwdddsawaasassdddwwdwasssaaawwdwwassdddssddwasaaawwddwdsaaawdsassddwsddwawaawasdddssawdwaaddwaaf</code></pre><p>The last step is to feed the solution steps into the netcat server. I generated a script (<code>feed_to_nc.py</code>) using Codex to do this, but it would be easy to do manually as well. Running the script, we can acquire the flag.</p>





<pre tabindex="0"><code>mmstoic@server:~/personal/tmp$ ./starless_c
There is a flag in the binary.
  (The flag is a metaphor but also still a flag.)
  (The binary could rightly be considered a gimmick.)
A person this far into a challenge has their path to follow. There were many paths, once, in a time that is past, lost many bytes and pages ago. Now there is only one path for you to choose. The path that leads to the flag.
lactf{starless_c_more_like_starless_0xcc}</code></pre><h2 id="remediation">Remediation</h2>
<p>This is your classic CTF reversing challenge that involves solving some kind of puzzle. However, if we pretend this binary was a piece of malware, reversing the binary can be made harder by using classic malware obfuscation techniques, like anti-reversing, packing, and anti-debugging tricks. Additionally, in the world of AI, using anti-LLM tricks to avoid automated analysis can highten the level of raw skill required to solve the challenge.</p>
<h1 id="sourcescredits">Sources/Credits</h1>
<p>Written by Madalina Stoicov</p>
]]></content:encoded></item></channel></rss>