<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://numberri.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://numberri.github.io/" rel="alternate" type="text/html" /><updated>2026-08-24T21:49:08+10:00</updated><id>https://numberri.github.io/feed.xml</id><title type="html">numberri</title><subtitle>blog for writeups and other stuff</subtitle><author><name>numberri</name></author><entry><title type="html">2026 BSides Brisbane Writeups - Starlet, Playshell, Tegra</title><link href="https://numberri.github.io/2026/08/24/BSides-Brisbane-writeups.html" rel="alternate" type="text/html" title="2026 BSides Brisbane Writeups - Starlet, Playshell, Tegra" /><published>2026-08-24T00:00:00+10:00</published><updated>2026-08-24T00:00:00+10:00</updated><id>https://numberri.github.io/2026/08/24/BSides-Brisbane-writeups</id><content type="html" xml:base="https://numberri.github.io/2026/08/24/BSides-Brisbane-writeups.html"><![CDATA[<script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type="text/javascript"></script>

<p>These are writeups for BSides Brisbane, which happened on July 4th! I hoped to get this out earlier, but I was <a href="https://wicc.ie/">flying out of the country</a> right after the conference, and then life happened.</p>

<p><br /></p>

<p>This was my first time on the other end of a CTF, making challenges instead of solving them. I started to think about making these in 2025 after Crikeycon, as there were not many crypto challenges.</p>

<p><br /></p>

<p>As small conference CTFs like this one tend to cater towards beginner players, I wanted to make some challenges that were related to what drew me to cryptography when I was first starting - a lot of <strong>real-world exploits on game consoles.</strong></p>

<p><br /></p>

<h1 id="starlet">Starlet</h1>
<p><a href="https://github.com/numberri/ctf-challenges/blob/main/bsides-bne-2026/starlet/dist/starlet.c">Challenge</a> <a href="https://github.com/numberri/ctf-challenges/blob/main/bsides-bne-2026/starlet/solution.py">Solution</a></p>

<p><br />
On a quick read through of the code, the program gives you the SHA-256 hash of a flag in hex, asks you what the flag is, and only gives you the flag if the hash of the two flags match.</p>

<p><br />
However, the function being used to compare the two hashes is <code class="language-plaintext highlighter-rouge">strncmp</code>. The <code class="language-plaintext highlighter-rouge">man</code> page for this function mentions that the function is equivalent to:
<br /></p>

<p><code class="language-plaintext highlighter-rouge">memcmp(s1, s2, MIN(MIN(strnlen(s1,n),strnlen(s2,n))+1, n))</code></p>

<p><br />
If you have ever worked with C before, you know that a string contains the characters in the string, then <strong>a null byte to show the string is finished</strong>. Looking at the output of the program:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>I will only show the flag to people who already know it!
The hash of the flag is: 816f0095a0d01aaf28541b830ac4ee0ef97d340e18c2611c6e60fe0ba8598df3
</code></pre></div></div>

<p><br />
In C, this will be read as <code class="language-plaintext highlighter-rouge">0x81 0x6f 0x00</code>… or a 2 character string! This means that instead of finding a full hash collision, you only need to find a collision of the first 3 bytes.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
    <span class="n">false_flag</span> <span class="o">=</span> <span class="nf">hex</span><span class="p">(</span><span class="n">i</span><span class="p">)[</span><span class="mi">2</span><span class="p">:]</span>
    <span class="nb">hash</span> <span class="o">=</span> <span class="nf">sha256</span><span class="p">(</span><span class="n">false_flag</span><span class="p">.</span><span class="nf">encode</span><span class="p">()).</span><span class="nf">digest</span><span class="p">()</span>
    <span class="k">if</span> <span class="nb">hash</span><span class="p">[:</span><span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="n">hash_bytes</span><span class="p">[:</span><span class="mi">3</span><span class="p">]:</span>
        <span class="k">break</span>
    <span class="n">i</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="nf">print</span><span class="p">(</span><span class="n">false_flag</span><span class="p">)</span>
</code></pre></div></div>
<p><br />
Once you have a flag that matches the first 3 bytes of the hash, the program will happily send you the flag you definitely already know :)</p>

<p><br /></p>
<h2 id="real-world-inspiration">Real-world inspiration</h2>
<p>This is based of of Trucha, the or signing bug, in the Nintendo Wii! It would verify the hash used in the signature of a disc or program, but used strncmp instead of memcmp, the same as this program. This made it quite easy to fake a signature. The Wiibrew wiki has a good article about it <a href="https://wiibrew.org/wiki/Signing_bug">here</a>.</p>

<p><br /></p>

<h1 id="playshell">Playshell</h1>
<p><a href="https://github.com/numberri/ctf-challenges/blob/main/bsides-bne-2026/playshell/dist/playshell.py">Challenge</a> <a href="https://github.com/numberri/ctf-challenges/blob/main/bsides-bne-2026/playshell/solution.py">Solution</a></p>

<p><br />
The core of this challenge is a nonce-reuse attack for ECDSA. The code used to generate a new signature is:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">sign</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">k</span> <span class="o">=</span> <span class="nf">randrange</span><span class="p">(</span><span class="nf">int</span><span class="p">(</span><span class="n">curve</span><span class="p">.</span><span class="nf">p</span><span class="p">()))):</span>
    <span class="nb">hash</span> <span class="o">=</span> <span class="nb">int</span><span class="p">.</span><span class="nf">from_bytes</span><span class="p">(</span><span class="nf">sha256</span><span class="p">(</span><span class="n">message</span><span class="p">.</span><span class="nf">encode</span><span class="p">()).</span><span class="nf">digest</span><span class="p">())</span>
    <span class="n">signature</span> <span class="o">=</span> <span class="n">key</span><span class="p">.</span><span class="nf">sign</span><span class="p">(</span><span class="nb">hash</span><span class="p">,</span> <span class="n">k</span><span class="p">)</span>
    <span class="nf">return </span><span class="p">(</span><span class="nf">int</span><span class="p">(</span><span class="n">signature</span><span class="p">.</span><span class="n">r</span><span class="p">),</span> <span class="nf">int</span><span class="p">(</span><span class="n">signature</span><span class="p">.</span><span class="n">s</span><span class="p">))</span>
</code></pre></div></div>

<p><br /> 
Since the random value is generated in the function header, the same value is used every time. You can also notice this by getting the signature for several different commands - the \(r\) value will always be the same.</p>

<p><br />
If you aren’t familiar with how ECDSA works - the signer takes \(G\), the generator part of the public key, and multiplies the point by \(k\). the \(x\) value of this point \(kG\) becomes the \(r\) value, and \(s\) is calculated as \(k^{-1}(z+rd)\), where \(d\) is the private key.</p>

<p><br />
However, when two of the same keys are used, the \(r\) value will be the same, and when the value of \(k\) and \(x\) is the same for both messages, you can recover the \(k\) value used with a bit of rearranging. Using that, you can recover the private key.</p>

<p><br />
After you have the private key, you can sign and register any command you want… for example, <code class="language-plaintext highlighter-rouge">cat flag.txt</code> :)</p>

<p><br /></p>
<h2 id="real-world-inspiration-1">Real-world inspiration</h2>
<p>This exploit was used to reveal the private key used to sign software on the Playstation 3! The PS3 used ECDSA internally to verify that software was legitimate and official - but they reused the same value of k, and in 2010 fail0verflow did a CCC talk on the vulnerability. You can <a href="https://youtu.be/DUGGJpn2_zY">watch the talk here</a> or <a href="https://fahrplan.events.ccc.de/congress/2010/Fahrplan/events/4087.en.html">look at the slides here</a>.</p>

<p><br /></p>
<h1 id="tegra">Tegra</h1>
<p><a href="https://github.com/numberri/ctf-challenges/blob/main/bsides-bne-2026/tegra/dist/tegra.py">Challenge</a> <a href="https://github.com/numberri/ctf-challenges/blob/main/bsides-bne-2026/tegra/solution.py">Solution</a></p>

<p><br />
Although the program doesn’t let you decrypt any text, the way that it replaces the key is vulnerable! When you provide a new key to encrypt things with, if the key is shorter than 16 bytes, then <strong>it keeps the rest of the old key</strong>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">replace_key</span><span class="p">(</span><span class="n">old</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">,</span> <span class="n">new</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bytes</span><span class="p">:</span>
    <span class="nf">return </span><span class="p">(</span><span class="n">old</span><span class="p">[</span><span class="nf">len</span><span class="p">(</span><span class="n">new</span><span class="p">):]</span> <span class="o">+</span> <span class="n">new</span><span class="p">).</span><span class="nf">ljust</span><span class="p">(</span><span class="mi">16</span><span class="p">,</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x00</span><span class="sh">"</span><span class="p">)[:</span><span class="mi">16</span><span class="p">]</span>
</code></pre></div></div>

<p><br />
Because of this, you can send a 1 byte key and keep 15 bytes of the old key, a 2 byte key and keep 14 bytes… If you send something to encrypt before you overwrite the key each time, once you replace all but one of the bytes, you can try to find what the last byte is by brute forcing that byte until you get a key that decrypts your text.</p>

<p><br />
Repeat this process with the unknown byte for every key, and once you’re finished, you can recover the full key - with a computational complexity of \(2^{12}\), instead of having to brute force the entire 256 bits.</p>

<p><br /></p>
<h2 id="real-world-inspiration-2">Real-world Inspiration</h2>
<p>When writing to the security engine of the Tegra X1 on the Switch, the AES keytable is written to 4 bytes at a time and immediately flushed. This has been used in the past to recover keys from the console. There is a little more information about it <a href="https://switchbrew.org/wiki/Switch_System_Flaws#Hardware">on the SwitchBrew wiki</a>, and a paper about the attack <a href="https://eprint.iacr.org/2025/570">here</a>.</p>

<p><br /></p>
<h2 id="final-notes">Final notes</h2>

<p><br />
BSides Brisbane had an AI category and a non-AI category, so my main goal in making these challenges is to get at least one person to try a challenge and become excited about cryptography. This was a massive success - I had a few people come tell me they thought my challenges were cool after the CTF, and one of them has been doing more crypto after that.</p>

<p><br />
As long as people keep learning from my challenges and having fun solving them, I’ll keep writing them - that’s the whole point of CTFs to me :)</p>]]></content><author><name>numberri</name></author><category term="writeups," /><category term="bsides_brisbane," /><category term="cryptography" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">2026 OICC Qualifiers - Baby Shark</title><link href="https://numberri.github.io/2026/02/04/OICC-Qualifiers-2026-Baby-Shark.html" rel="alternate" type="text/html" title="2026 OICC Qualifiers - Baby Shark" /><published>2026-02-04T00:00:00+10:00</published><updated>2026-02-04T00:00:00+10:00</updated><id>https://numberri.github.io/2026/02/04/OICC-Qualifiers-2026-Baby-Shark</id><content type="html" xml:base="https://numberri.github.io/2026/02/04/OICC-Qualifiers-2026-Baby-Shark.html"><![CDATA[<script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type="text/javascript"></script>

<p>It’s been a while since I’ve written on this blog! Since I last posted, I’ve gotten better at crypto challenges, and was able to solve one for the <a href="https://oceaniacc.com/">Team Oceania</a> Qualifiers this year.</p>

<p><br /></p>

<h2 id="the-challenge">The Challenge</h2>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">fastecdsa</span> <span class="kn">import</span> <span class="n">curve</span><span class="p">,</span> <span class="n">ecdsa</span><span class="p">,</span> <span class="n">keys</span>
<span class="kn">import</span> <span class="n">ast</span><span class="p">,</span> <span class="n">os</span>

<span class="n">FLAG</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="sh">'</span><span class="s">FLAG</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">oiccflag{?????????????????????????????????????}</span><span class="sh">'</span><span class="p">)</span>

<span class="n">p</span> <span class="o">=</span> <span class="p">(</span><span class="mi">3</span><span class="o">&lt;&lt;</span><span class="mi">256</span><span class="p">)</span> <span class="o">-</span> <span class="p">(</span><span class="mi">3</span><span class="o">&lt;&lt;</span><span class="mi">128</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span>
<span class="n">EC</span> <span class="o">=</span> <span class="n">curve</span><span class="p">.</span><span class="nc">Curve</span><span class="p">(</span><span class="sh">"</span><span class="s">BabyShark258</span><span class="sh">"</span><span class="p">,</span> <span class="n">p</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">22</span><span class="p">,</span> <span class="n">p</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">7</span><span class="p">)</span>
<span class="n">priv</span><span class="p">,</span> <span class="n">pub</span> <span class="o">=</span> <span class="n">keys</span><span class="p">.</span><span class="nf">gen_keypair</span><span class="p">(</span><span class="n">EC</span><span class="p">)</span>

<span class="n">msgs</span> <span class="o">=</span> <span class="p">[</span><span class="sh">'</span><span class="s">Baby shark</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Doo doo doo doo doo doo</span><span class="sh">'</span><span class="p">]</span>
<span class="k">for</span> <span class="n">msg</span> <span class="ow">in</span> <span class="n">msgs</span><span class="p">:</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">'</span><span class="si">{</span><span class="n">msg</span><span class="si">}</span><span class="s">: </span><span class="si">{</span><span class="n">ecdsa</span><span class="p">.</span><span class="nf">sign</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="n">priv</span><span class="p">,</span> <span class="n">EC</span><span class="p">)</span><span class="si">}</span><span class="sh">'</span><span class="p">)</span>
<span class="n">sig</span> <span class="o">=</span> <span class="n">ast</span><span class="p">.</span><span class="nf">literal_eval</span><span class="p">(</span><span class="nf">input</span><span class="p">(</span><span class="sh">'</span><span class="s">Combined signature: </span><span class="sh">'</span><span class="p">))</span>
<span class="nf">print</span><span class="p">(</span><span class="nf">all</span><span class="p">(</span><span class="n">ecdsa</span><span class="p">.</span><span class="nf">verify</span><span class="p">(</span><span class="n">sig</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="n">pub</span><span class="p">,</span> <span class="n">EC</span><span class="p">)</span> <span class="k">for</span> <span class="n">msg</span> <span class="ow">in</span> <span class="n">msgs</span><span class="p">)</span> <span class="ow">and</span> <span class="n">FLAG</span><span class="p">)</span>
</code></pre></div></div>

<p><br /></p>

<p>First thing I notice - we aren’t given the public key, but with either one of the signatures, it’s possible to recover. I used some of the code from the <code class="language-plaintext highlighter-rouge">recover_public_keys</code> function in the <a href="https://github.com/tlsfuzzer/python-ecdsa/blob/master/src/ecdsa/ecdsa.py">python ecdsa library</a>, modified to work with the custom curve.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">ecdsa</span> <span class="kn">import</span> <span class="n">numbertheory</span>
<span class="kn">from</span> <span class="n">hashlib</span> <span class="kn">import</span> <span class="n">sha256</span>

<span class="k">def</span> <span class="nf">msg_bytes</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bytes</span><span class="p">:</span>
    <span class="k">if</span> <span class="nf">isinstance</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="nb">bytes</span><span class="p">):</span>
        <span class="k">return</span> <span class="n">msg</span>
    <span class="k">elif</span> <span class="nf">isinstance</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
        <span class="k">return</span> <span class="n">msg</span><span class="p">.</span><span class="nf">encode</span><span class="p">()</span>
    <span class="k">elif</span> <span class="nf">isinstance</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="nb">bytearray</span><span class="p">):</span>
        <span class="k">return</span> <span class="nf">bytes</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">recover_pubkey</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">msg</span><span class="p">):</span>
    <span class="n">x</span> <span class="o">=</span> <span class="n">r</span>
    <span class="n">e</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="nf">sha256</span><span class="p">(</span><span class="nf">msg_bytes</span><span class="p">(</span><span class="n">msg</span><span class="p">)).</span><span class="nf">hexdigest</span><span class="p">(),</span> <span class="mi">16</span><span class="p">)</span>

    <span class="n">alpha</span> <span class="o">=</span> <span class="p">(</span><span class="nf">pow</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="n">p</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">a</span> <span class="o">*</span> <span class="n">x</span><span class="p">)</span> <span class="o">+</span> <span class="n">b</span><span class="p">)</span> <span class="o">%</span> <span class="n">p</span>
    <span class="n">beta</span> <span class="o">=</span> <span class="n">numbertheory</span><span class="p">.</span><span class="nf">square_root_mod_prime</span><span class="p">(</span><span class="n">alpha</span><span class="p">,</span> <span class="n">p</span><span class="p">)</span>
    <span class="n">y</span> <span class="o">=</span> <span class="n">beta</span> <span class="k">if</span> <span class="n">beta</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span> <span class="k">else</span> <span class="n">p</span> <span class="o">-</span> <span class="n">beta</span>

    <span class="c1"># Compute the public key
</span>    <span class="n">R1</span> <span class="o">=</span> <span class="nc">E</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
    <span class="n">Q1</span> <span class="o">=</span> <span class="nf">pow</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">s</span> <span class="o">*</span> <span class="n">R1</span> <span class="o">+</span> <span class="p">(</span><span class="o">-</span><span class="n">e</span> <span class="o">%</span> <span class="n">n</span><span class="p">)</span> <span class="o">*</span> <span class="n">G</span><span class="p">)</span>
    <span class="n">Pk1</span> <span class="o">=</span> <span class="n">Q1</span>

    <span class="c1"># And the second solution
</span>    <span class="n">R2</span> <span class="o">=</span> <span class="nc">E</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="o">-</span><span class="n">y</span><span class="p">)</span>
    <span class="n">Q2</span> <span class="o">=</span> <span class="nf">pow</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">s</span> <span class="o">*</span> <span class="n">R2</span> <span class="o">+</span> <span class="p">(</span><span class="o">-</span><span class="n">e</span> <span class="o">%</span> <span class="n">n</span><span class="p">)</span> <span class="o">*</span> <span class="n">G</span><span class="p">)</span>
    <span class="n">Pk2</span> <span class="o">=</span> <span class="n">Q2</span>

    <span class="k">return</span> <span class="p">[</span><span class="n">Pk1</span><span class="p">,</span> <span class="n">Pk2</span><span class="p">]</span>
</code></pre></div></div>

<p>For both signatures, one of the valid public keys was shared, and that is the public key we need.</p>

<p><br /></p>

<p>Second thing to notice - <strong>this curve is anomolous!</strong> This means that the order of the generator point is the same as the curve order, and it is possible to use Smart’s Attack. This means that the points on the elliptic curve can be “lifted” to a curve defined over p-adic numbers, and you can then recover the private key. <a href="https://github.com/elikaski/ECC_Attacks?tab=readme-ov-file#The-curve-is-anomalous">This github page</a> has a good example of the attack, which I used.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">lift</span><span class="p">(</span><span class="n">P</span><span class="p">,</span> <span class="n">E</span><span class="p">,</span> <span class="n">p</span><span class="p">):</span>
    <span class="c1"># lift point P from old curve to a new curve
</span>    <span class="n">Px</span><span class="p">,</span> <span class="n">Py</span> <span class="o">=</span> <span class="nf">map</span><span class="p">(</span><span class="n">ZZ</span><span class="p">,</span> <span class="n">P</span><span class="p">.</span><span class="nf">xy</span><span class="p">())</span>
    <span class="k">for</span> <span class="n">point</span> <span class="ow">in</span> <span class="n">E</span><span class="p">.</span><span class="nf">lift_x</span><span class="p">(</span><span class="n">Px</span><span class="p">,</span> <span class="nb">all</span><span class="o">=</span><span class="bp">True</span><span class="p">):</span>
         <span class="c1"># take the matching one of the 2 points corresponding to this x on the p-adic curve
</span>        <span class="n">_</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="nf">map</span><span class="p">(</span><span class="n">ZZ</span><span class="p">,</span> <span class="n">point</span><span class="p">.</span><span class="nf">xy</span><span class="p">())</span>
        <span class="k">if</span> <span class="n">y</span> <span class="o">%</span> <span class="n">p</span> <span class="o">==</span> <span class="n">Py</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">point</span>

<span class="n">P</span> <span class="o">=</span> <span class="n">pubkey</span>
<span class="n">E_adic</span> <span class="o">=</span> <span class="nc">EllipticCurve</span><span class="p">(</span><span class="nc">Qp</span><span class="p">(</span><span class="n">p</span><span class="p">),</span> <span class="p">[</span><span class="n">a</span><span class="o">+</span><span class="n">p</span><span class="o">*</span><span class="mi">13</span><span class="p">,</span> <span class="n">b</span><span class="o">+</span><span class="n">p</span><span class="o">*</span><span class="mi">37</span><span class="p">])</span>
<span class="n">newG</span> <span class="o">=</span> <span class="n">p</span> <span class="o">*</span> <span class="nf">lift</span><span class="p">(</span><span class="n">G</span><span class="p">,</span> <span class="n">E_adic</span><span class="p">,</span> <span class="n">p</span><span class="p">)</span>
<span class="n">P</span> <span class="o">=</span> <span class="n">p</span> <span class="o">*</span> <span class="nf">lift</span><span class="p">(</span><span class="n">P</span><span class="p">,</span> <span class="n">E_adic</span><span class="p">,</span> <span class="n">p</span><span class="p">)</span>

<span class="c1"># Calculate discrete log
</span><span class="n">Gx</span><span class="p">,</span> <span class="n">Gy</span> <span class="o">=</span> <span class="n">newG</span><span class="p">.</span><span class="nf">xy</span><span class="p">()</span>
<span class="n">Px</span><span class="p">,</span> <span class="n">Py</span> <span class="o">=</span> <span class="n">P</span><span class="p">.</span><span class="nf">xy</span><span class="p">()</span>
<span class="n">d</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="nc">GF</span><span class="p">(</span><span class="n">p</span><span class="p">)((</span><span class="n">Px</span> <span class="o">/</span> <span class="n">Py</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="n">Gx</span> <span class="o">/</span> <span class="n">Gy</span><span class="p">)))</span>
<span class="k">assert</span> <span class="n">pubkey</span> <span class="o">==</span> <span class="n">d</span> <span class="o">*</span> <span class="n">G</span>
</code></pre></div></div>

<p><br /></p>

<p>After getting the private key, the last challenge is making a signature using a chosen value \(k\) - instead of a random one - where \((r, s)\) is the same for the hashes of both messages, denoted \(z_1\) and \(z_2\).</p>

<p><br /></p>

<p>As \(k=z+r\cdot d\), when using \(k_2=k_1^{-1}\), it is possible to force the same value of $r$ by solving for \(r\) when \(\frac{k_1}{k_2} = \frac{z_1 + r\cdot d}{z_2 + r\cdot d}\). After re-arranging, you find \(k_1\) (and \(k_2\), as it is the inverse of \(k_1\)) by lifting \(r\) to the p-adic curve - finding \(k_1\) the same way as the private key. With this crafted value of \(k\), you can calculate \(s\) as normal, and it will a valid signature for both \(z_1\) and \(z_2\).</p>

<p><br /></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">msgs</span> <span class="o">=</span> <span class="p">[</span><span class="sh">"</span><span class="s">Baby shark</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">Doo doo doo doo doo doo</span><span class="sh">"</span><span class="p">]</span>

<span class="n">z1</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="nf">sha256</span><span class="p">(</span><span class="nf">msg_bytes</span><span class="p">(</span><span class="n">msgs</span><span class="p">[</span><span class="mi">0</span><span class="p">])).</span><span class="nf">hexdigest</span><span class="p">(),</span> <span class="mi">16</span><span class="p">)</span>
<span class="n">z2</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="nf">sha256</span><span class="p">(</span><span class="nf">msg_bytes</span><span class="p">(</span><span class="n">msgs</span><span class="p">[</span><span class="mi">1</span><span class="p">])).</span><span class="nf">hexdigest</span><span class="p">(),</span> <span class="mi">16</span><span class="p">)</span>

<span class="n">r</span> <span class="o">=</span> <span class="o">-</span><span class="p">(</span><span class="n">z1</span> <span class="o">+</span> <span class="n">z2</span><span class="p">)</span> <span class="o">*</span> <span class="nf">pow</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">d</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">p</span><span class="p">)</span> <span class="o">%</span> <span class="n">p</span>
<span class="n">R</span> <span class="o">=</span> <span class="n">E</span><span class="p">.</span><span class="nf">lift_x</span><span class="p">(</span><span class="n">r</span><span class="p">)</span>
<span class="n">Rx</span><span class="p">,</span> <span class="n">Ry</span> <span class="o">=</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span> <span class="nf">lift</span><span class="p">(</span><span class="n">R</span><span class="p">,</span> <span class="n">E_adic</span><span class="p">,</span> <span class="n">p</span><span class="p">)).</span><span class="nf">xy</span><span class="p">()</span>
<span class="n">k1</span> <span class="o">=</span> <span class="nc">ZZ</span><span class="p">(</span><span class="o">-</span><span class="p">(</span><span class="n">Rx</span> <span class="o">/</span> <span class="n">Ry</span><span class="p">)</span> <span class="o">/</span> <span class="o">-</span><span class="p">(</span><span class="n">Gx</span> <span class="o">/</span> <span class="n">Gy</span><span class="p">))</span> <span class="o">%</span> <span class="n">p</span>

<span class="n">r1</span> <span class="o">=</span> <span class="p">(</span><span class="n">k1</span> <span class="o">*</span> <span class="n">G</span><span class="p">).</span><span class="nf">xy</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span> <span class="o">%</span> <span class="n">p</span>
<span class="n">s</span> <span class="o">=</span> <span class="nf">pow</span><span class="p">(</span><span class="n">k1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">p</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">z1</span> <span class="o">+</span> <span class="n">r1</span> <span class="o">*</span> <span class="n">d</span><span class="p">)</span> <span class="o">%</span> <span class="n">p</span>

<span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">(</span><span class="sh">"</span> <span class="o">+</span> <span class="nf">str</span><span class="p">(</span><span class="n">r1</span><span class="p">)</span> <span class="o">+</span> <span class="sh">"</span><span class="s">, </span><span class="sh">"</span> <span class="o">+</span> <span class="nf">str</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">+</span> <span class="sh">"</span><span class="s">)</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<p>As the signature is valid for both messages, submitting the signature to the server will result in a flag :)</p>

<p><br /></p>

<p>The full code can be found <a href="https://gist.github.com/numberri/23a50c2ac525a0aec24bb75c7d522f44">on this gist</a>.</p>]]></content><author><name>numberri</name></author><category term="writeups," /><category term="oicc," /><category term="cryptography" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">CrikeyCon 2025 - I CHOOSE YOU</title><link href="https://numberri.github.io/2025/03/24/Crikeycon-X-CTF-I_CHOOSE_YOU.html" rel="alternate" type="text/html" title="CrikeyCon 2025 - I CHOOSE YOU" /><published>2025-03-24T00:00:00+10:00</published><updated>2025-03-24T00:00:00+10:00</updated><id>https://numberri.github.io/2025/03/24/Crikeycon-X-CTF-I_CHOOSE_YOU</id><content type="html" xml:base="https://numberri.github.io/2025/03/24/Crikeycon-X-CTF-I_CHOOSE_YOU.html"><![CDATA[<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Category: Crypto
Points: 100
Solves: 3
</code></pre></div></div>
<p><br /></p>

<p><strong><em>Prove you are timk:</em></strong></p>

<p><br /></p>

<p><em>I’ve chosen a Pokemon and encrypted it using timk’s public key.</em></p>

<p><em>To prove you are timk, use your private key to decrypt the ciphertext which will tell you the Pokemon to select.</em></p>

<p><em>I know timk has lots of keypairs so I’ve supplied the public key for him to know which private key to use.</em></p>

<p><br /></p>

<p><em>To ensure things stay safe, I’ll rotate the Pokemon and key every 5 minutes.</em></p>

<p><em>Don’t bother trying to guess as I will block you for 30 seconds on an incorrect attempt!</em></p>

<p><br /></p>

<h1 id="the-go-plan---and-where-i-mess-things-up">The go plan - and where I mess things up</h1>

<p>Upon inspecting the website, this comment is also revealed:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;!-- TODO: Look into newer padding schemes. For now I have just padded PT to the key size using leading null bytes, but it should be pretty solid. --&gt;</span>
</code></pre></div></div>

<p><br /></p>

<p>Pretty solid… of an attack strategy :)</p>

<p><br /></p>

<p>The TL;DR of what needs to be done is:</p>
<ul>
  <li>Take the public key provided and the list of all 808 Pokemon.</li>
  <li>Pad the Pokemon with leading null bytes, and then encrypt that string with the public key provided.</li>
  <li>Compare this ciphertext with the encrypted Pokemon provided. If you find a match, then you have the Pokemon!</li>
</ul>

<p><br /></p>

<p>Pretty easy! First problem I run into… parsing the .pem key to an RSA public key. I opt to pass this to openssl:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">pad_with_null_bytes</span><span class="p">(</span><span class="n">pokemon</span><span class="p">):</span>
    <span class="n">max_data_size</span> <span class="o">=</span> <span class="mi">244</span> <span class="c1"># This was first done by trial and error...
</span>    <span class="c1"># I found out there was a reason why this was 244 and not 256 later, which was why my code was failing. :P
</span>    <span class="n">padding_length</span> <span class="o">=</span> <span class="n">max_data_size</span> <span class="o">-</span> <span class="nf">len</span><span class="p">(</span><span class="n">pokemon</span><span class="p">)</span>
    <span class="n">padded</span> <span class="o">=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x00</span><span class="sh">"</span> <span class="o">*</span> <span class="n">padding_length</span> <span class="o">+</span> <span class="n">pokemon</span>
    <span class="k">return</span> <span class="n">padded</span>

<span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">character.enc</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">rb</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="nb">file</span><span class="p">:</span>
    <span class="n">encrypted_solution</span> <span class="o">=</span> <span class="nb">file</span><span class="p">.</span><span class="nf">read</span><span class="p">().</span><span class="nf">strip</span><span class="p">()</span>

<span class="n">pokemon</span> <span class="o">=</span> <span class="nf">open</span><span class="p">(</span><span class="sh">"</span><span class="s">pokemon_list.txt</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">r</span><span class="sh">"</span><span class="p">)</span>

<span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">pokemon</span><span class="p">:</span>
    <span class="n">p</span> <span class="o">=</span> <span class="nf">pad_with_null_bytes</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="nf">strip</span><span class="p">())</span>
    <span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">current.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">w</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">current</span><span class="p">:</span>
        <span class="n">current</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
    
    <span class="n">os</span><span class="p">.</span><span class="nf">system</span><span class="p">(</span><span class="sh">'</span><span class="s">cat current.txt | openssl pkeyutl -encrypt -pubin -inkey public.pem &gt; text.enc</span><span class="sh">'</span><span class="p">)</span>
    <span class="c1"># This was a lot messier and had full paths, but I'm shortening it for privacy and to make it easier to read
</span>
    <span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">text.enc</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">rb</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">c</span><span class="p">:</span>
        <span class="n">pokemon_encrypted</span> <span class="o">=</span> <span class="n">c</span><span class="p">.</span><span class="nf">read</span><span class="p">()</span>

    <span class="k">if</span> <span class="n">pokemon_encrypted</span> <span class="o">==</span> <span class="n">encrypted_solution</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>

</code></pre></div></div>

<p><br /></p>

<p>Great! Code to encrypt Pokemon done! And yet… I wasn’t getting any matches.</p>

<p><br /></p>

<h1 id="a-bit-of-hindsight-and-more-mistakes">A bit of hindsight, and more mistakes</h1>
<p>I think that a common theme of write-ups on this blog so far has been “I spend too long barking up the wrong tree, and I should have figured out what was going on earlier”.</p>

<p><br /></p>

<p>What was actually happening was I was double-padding the text! Most libraries that encrypt things using RSA public keys hope that you use sane and secure padding, which is fantastic for IRL implementations, however for this CTF challenge it wasn’t what I wanted.</p>

<p><br /></p>

<p>This is also why I couldn’t encrypt things when the plaintext was longer than 244 bytes, instead of the 256 byte key length I expected - it’s because of PKCS#1 v1.5 padding overhead.</p>

<p><br /></p>

<p>I unfortunately didn’t realize this, and spent more time writing incorrect code, and a LOT of time debugging it.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">cryptography.hazmat.primitives.asymmetric</span> <span class="kn">import</span> <span class="n">padding</span>
<span class="kn">from</span> <span class="n">cryptography.hazmat.primitives</span> <span class="kn">import</span> <span class="n">serialization</span>

<span class="k">def</span> <span class="nf">load_public_key</span><span class="p">(</span><span class="n">pem_path</span><span class="p">):</span>
    <span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="n">pem_path</span><span class="p">,</span> <span class="sh">"</span><span class="s">rb</span><span class="sh">"</span><span class="p">)</span> <span class="k">as</span> <span class="n">key_file</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">serialization</span><span class="p">.</span><span class="nf">load_pem_public_key</span><span class="p">(</span><span class="n">key_file</span><span class="p">.</span><span class="nf">read</span><span class="p">())</span>
    <span class="c1"># It was good to find a library that let me load a public key in Python, but for future reference I should understand it more before using it. :)
</span>
<span class="k">def</span> <span class="nf">pad_with_null_bytes</span><span class="p">(</span><span class="n">pokemon</span><span class="p">):</span>
    <span class="n">max_data_size</span> <span class="o">=</span> <span class="mi">244</span>
    <span class="n">padding_length</span> <span class="o">=</span> <span class="n">max_data_size</span> <span class="o">-</span> <span class="nf">len</span><span class="p">(</span><span class="n">pokemon</span><span class="p">)</span>
    <span class="n">padded</span> <span class="o">=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x00</span><span class="sh">"</span> <span class="o">*</span> <span class="n">padding_length</span> <span class="o">+</span> <span class="n">pokemon</span>
    <span class="k">return</span> <span class="n">padded</span>

<span class="k">def</span> <span class="nf">encrypt_pokemon</span><span class="p">(</span><span class="n">public_key</span><span class="p">,</span> <span class="n">pokemon</span><span class="p">):</span>
    <span class="n">key_size</span> <span class="o">=</span> <span class="n">public_key</span><span class="p">.</span><span class="n">key_size</span> <span class="o">//</span> <span class="mi">8</span>  
    <span class="c1"># Converting the RSA key size from bits to bytes.
</span>    <span class="n">padded</span> <span class="o">=</span> <span class="nf">pad_with_null_bytes</span><span class="p">(</span><span class="n">pokemon</span><span class="p">)</span>

    <span class="n">encrypted</span> <span class="o">=</span> <span class="n">public_key</span><span class="p">.</span><span class="nf">encrypt</span><span class="p">(</span>
        <span class="n">padded</span><span class="p">,</span>
        <span class="n">padding</span><span class="p">.</span><span class="nc">PKCS1v15</span><span class="p">()</span>
        <span class="c1"># I definitely knew that it was double padding at this point, but I didn't know how the library worked so trying to not pad this threw errors.
</span>        <span class="c1"># At some point, I even had the comment of "how do I get rid of this shit"...
</span>    <span class="p">)</span>
    <span class="k">return</span> <span class="n">encrypted</span>

<span class="n">pubkey</span> <span class="o">=</span> <span class="nf">load_public_key</span><span class="p">(</span><span class="sh">"</span><span class="s">public.pem</span><span class="sh">"</span><span class="p">)</span>

<span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">character.enc</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">rb</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="nb">file</span><span class="p">:</span>
    <span class="n">encrypted_solution</span> <span class="o">=</span> <span class="nb">file</span><span class="p">.</span><span class="nf">read</span><span class="p">().</span><span class="nf">strip</span><span class="p">()</span>
<span class="n">pokemon</span> <span class="o">=</span> <span class="nf">open</span><span class="p">(</span><span class="sh">"</span><span class="s">pokemon_list.txt</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">r</span><span class="sh">"</span><span class="p">)</span>

<span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">pokemon</span><span class="p">:</span>
    <span class="n">p</span> <span class="o">=</span> <span class="nf">pad_with_null_bytes</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="nf">strip</span><span class="p">())</span>
    <span class="n">pokemon_encrypted</span> <span class="o">=</span> <span class="nf">encrypt_pokemon</span><span class="p">(</span><span class="n">pubkey</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="nf">encode</span><span class="p">())</span>
    <span class="k">if</span> <span class="n">pokemon_encrypted</span> <span class="o">==</span> <span class="n">encrypted_solution</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
</code></pre></div></div>

<p><br /></p>

<h1 id="the-solution-finally">The solution, finally</h1>
<p>Partially because I was running around the con and talking to people as well as doing the CTF, I only got the solution 10 minutes before the CTF ended.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">cryptography.hazmat.primitives</span> <span class="kn">import</span> <span class="n">serialization</span>

<span class="k">def</span> <span class="nf">load_public_key</span><span class="p">(</span><span class="n">pem_path</span><span class="p">):</span>
    <span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="n">pem_path</span><span class="p">,</span> <span class="sh">"</span><span class="s">rb</span><span class="sh">"</span><span class="p">)</span> <span class="k">as</span> <span class="n">key_file</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">serialization</span><span class="p">.</span><span class="nf">load_pem_public_key</span><span class="p">(</span><span class="n">key_file</span><span class="p">.</span><span class="nf">read</span><span class="p">())</span>

<span class="k">def</span> <span class="nf">encrypt_pokemon</span><span class="p">(</span><span class="n">public_key</span><span class="p">,</span> <span class="n">pokemon</span><span class="p">):</span>
    <span class="n">key_size</span> <span class="o">=</span> <span class="n">public_key</span><span class="p">.</span><span class="n">key_size</span> <span class="o">//</span> <span class="mi">8</span>
    <span class="c1"># Converting the RSA key size from bits to bytes.
</span>    <span class="n">encrypted</span> <span class="o">=</span> <span class="n">public_key</span><span class="p">.</span><span class="nf">public_numbers</span><span class="p">().</span><span class="n">n</span><span class="p">.</span><span class="nf">to_bytes</span><span class="p">(</span><span class="n">key_size</span><span class="p">,</span> <span class="sh">'</span><span class="s">big</span><span class="sh">'</span><span class="p">)</span>
    <span class="n">encrypted</span> <span class="o">=</span> <span class="nf">pow</span><span class="p">(</span><span class="nb">int</span><span class="p">.</span><span class="nf">from_bytes</span><span class="p">(</span><span class="n">pokemon</span><span class="p">,</span> <span class="sh">'</span><span class="s">big</span><span class="sh">'</span><span class="p">),</span> <span class="n">public_key</span><span class="p">.</span><span class="nf">public_numbers</span><span class="p">().</span><span class="n">e</span><span class="p">,</span> <span class="n">public_key</span><span class="p">.</span><span class="nf">public_numbers</span><span class="p">().</span><span class="n">n</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">encrypted</span><span class="p">.</span><span class="nf">to_bytes</span><span class="p">(</span><span class="n">key_size</span><span class="p">,</span> <span class="sh">'</span><span class="s">big</span><span class="sh">'</span><span class="p">)</span>

<span class="n">pubkey</span> <span class="o">=</span> <span class="nf">load_public_key</span><span class="p">(</span><span class="sh">"</span><span class="s">i_choose_you/public.pem</span><span class="sh">"</span><span class="p">)</span>

<span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">i_choose_you/character.enc</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">rb</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="nb">file</span><span class="p">:</span>
    <span class="n">encrypted_solution</span> <span class="o">=</span> <span class="nb">file</span><span class="p">.</span><span class="nf">read</span><span class="p">().</span><span class="nf">strip</span><span class="p">()</span>
<span class="n">pokemon</span> <span class="o">=</span> <span class="nf">open</span><span class="p">(</span><span class="sh">"</span><span class="s">i_choose_you/pokemon_list.txt</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">r</span><span class="sh">"</span><span class="p">)</span>

<span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">pokemon</span><span class="p">:</span>
    <span class="n">p</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="nf">strip</span><span class="p">()</span>
    <span class="n">tst</span> <span class="o">=</span> <span class="nf">encrypt_pokemon</span><span class="p">(</span><span class="n">pubkey</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="nf">encode</span><span class="p">())</span>
    <span class="k">if</span> <span class="n">tst</span> <span class="o">==</span> <span class="n">encrypted_solution</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
</code></pre></div></div>

<p><br /></p>

<p>And then…</p>

<p><br /></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># python3 ./final.py
Chimecho
</code></pre></div></div>

<p><br /></p>

<p>Finally! I’m able to submit the Pokemon with 10 minutes left in the CTF.</p>

<p><br /></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Well done! flag{                  }
</code></pre></div></div>

<p><br /></p>

<h1 id="finishing-notes-and-self-reflection">Finishing notes and self reflection</h1>
<p>I actually wasn’t planning on taking part in this CTF, however I got a text from one of the members from my uni society asking me to join the team as they need someone who understands crypto. I know I’ve always said that crypto doesn’t like me, but it turns out that kind of half understanding crypto is a pretty high bar. We ended up coming third in the CTF as well, which is incredible!</p>

<p><br /></p>

<p>On the flip side, I ended up meeting the crypto player of the winning team, as well. He ended up getting over half the points for his team, and he was pretty incredible…. I may have gotten a bit of a case of impostor syndrome after the CTF. I think that this is definitely going to encourage me to work harder on training for CTFs in the future, although this motivation may be killed by university work in the near future.</p>

<p>(also, you should check this guy out. <a href="https://jsur.in/">he’s pretty cool</a>.)</p>

<p><br /></p>

<p>Overall, I think that what I need to get better at is being able to script well, and knowing the libraries and ins and outs of the tools I use for CTFs. Unfortunately this is something where the solution is “just play more”, but I think that’s life.</p>]]></content><author><name>numberri</name></author><category term="writeups," /><category term="crikeycon," /><category term="cryptography" /><summary type="html"><![CDATA[Category: Crypto Points: 100 Solves: 3]]></summary></entry><entry><title type="html">ACECTF 1.0 - Harder Disk</title><link href="https://numberri.github.io/2025/03/03/ACECTF-1.0-harder-disk.html" rel="alternate" type="text/html" title="ACECTF 1.0 - Harder Disk" /><published>2025-03-03T00:00:00+10:00</published><updated>2025-03-03T00:00:00+10:00</updated><id>https://numberri.github.io/2025/03/03/ACECTF-1.0-harder-disk</id><content type="html" xml:base="https://numberri.github.io/2025/03/03/ACECTF-1.0-harder-disk.html"><![CDATA[<h2 id="alternative-tiltle-for-this-one-how-noob-ways-of-thinking-can-shoot-you-in-the-back">Alternative tiltle for this one: How noob ways of thinking can shoot you in the back</h2>
<p><br /></p>

<p><strong><em>One of the first things I learnt when I started learning to hack was linux. It was fun until I hit a ceiling of understanding about the differences in Operating Systems, what’s a Shell, Kernel, etc.</em></strong></p>

<p><br /></p>

<p><strong><em>But once I got better I started developing a liking towards the terminal and how the Linux operating system is better than say Windows, or worse in some cases. How none of them is superior, nor the other inferior. We shall find out with this challenge.</em></strong></p>

<p><br /></p>

<p><strong><em>Be careful, a lot of fake galfs around.</em></strong></p>

<p><br /></p>

<p>For the challenge you were given an unmarked file named <a href="https://drive.google.com/file/d/1tZv94aEKV4Mc33sJECWVqqAEPiXHhCd-/view">challenge</a>. Upon running <code class="language-plaintext highlighter-rouge">file challenge</code>, you see that it is a Windows file system.
<br />
<img src="https://numberri.github.io/assets/images/harder-disk/file_challenge.png" alt="Image" class="post-img" />
<br />
I change the file to <code class="language-plaintext highlighter-rouge">challenge.iso</code> and I mount it. Naively, I try to strings and grep for the flag:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for </span>i <span class="k">in</span> <span class="k">*</span><span class="p">;</span> <span class="k">do </span>strings <span class="s2">"</span><span class="nv">$i</span><span class="s2">"</span> | <span class="nb">grep</span> <span class="nt">-e</span> ACECTF<span class="p">;</span> <span class="k">done</span>
</code></pre></div></div>
<p><br />
With this I am rewarded with…</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ACECTF{50_much_f0r_50_l177l3}
&lt;x:xmpmeta xmlns:x="adobe:ns:meta/"&gt;&lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"&gt;&lt;rdf:Description rdf:about="uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b" xmlns:dc="http://purl.org/dc/elements/1.1/"&gt;&lt;dc:title&gt;&lt;rdf:Alt xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"&gt;&lt;rdf:li xml:lang="x-default"&gt;ACECTF{50_much_f0r_50_l177l3}&lt;/rdf:li&gt;&lt;/rdf:Alt&gt;
&lt;/dc:title&gt;&lt;/rdf:Description&gt;&lt;rdf:Description rdf:about="uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b" xmlns:dc="http://purl.org/dc/elements/1.1/"&gt;&lt;dc:description&gt;&lt;rdf:Alt xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"&gt;&lt;rdf:li xml:lang="x-default"&gt;ACECTF{50_much_f0r_50_l177l3}&lt;/rdf:li&gt;&lt;/rdf:Alt&gt;
ACECTF{50_much_f0r_50_l177l3}
&lt;x:xmpmeta xmlns:x="adobe:ns:meta/"&gt;&lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"&gt;&lt;rdf:Description rdf:about="uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b" xmlns:dc="http://purl.org/dc/elements/1.1/"&gt;&lt;dc:title&gt;&lt;rdf:Alt xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"&gt;&lt;rdf:li xml:lang="x-default"&gt;ACECTF{50_much_f0r_50_l177l3}&lt;/rdf:li&gt;&lt;/rdf:Alt&gt;
&lt;/dc:title&gt;&lt;/rdf:Description&gt;&lt;rdf:Description rdf:about="uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b" xmlns:dc="http://purl.org/dc/elements/1.1/"&gt;&lt;dc:description&gt;&lt;rdf:Alt xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"&gt;&lt;rdf:li xml:lang="x-default"&gt;ACECTF{50_much_f0r_50_l177l3}&lt;/rdf:li&gt;&lt;/rdf:Alt&gt;
ACECTF{What are you looking at?}
&lt;x:xmpmeta xmlns:x="adobe:ns:meta/"&gt;&lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"&gt;&lt;rdf:Description rdf:about="uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b" xmlns:dc="http://purl.org/dc/elements/1.1/"&gt;&lt;dc:title&gt;&lt;rdf:Alt xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"&gt;&lt;rdf:li xml:lang="x-default"&gt;ACECTF{What are you looking at?}&lt;/rdf:li&gt;&lt;/rdf:Alt&gt;
&lt;/dc:title&gt;&lt;/rdf:Description&gt;&lt;rdf:Description rdf:about="uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b" xmlns:dc="http://purl.org/dc/elements/1.1/"&gt;&lt;dc:description&gt;&lt;rdf:Alt xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"&gt;&lt;rdf:li xml:lang="x-default"&gt;ACECTF{What are you looking at?}&lt;/rdf:li&gt;&lt;/rdf:Alt&gt;
ACECTF{4ll_7h47_ju57_70_g37_f4k3_64lf}
ACECTF{4ll_7h47_ju57_70_g37_f4k3_64lf}
ACECTF{4ll_7h47_ju57_70_g37_f4k3_64lf}
ACECTF{4ll_7h47_ju57_70_g37_f4k3_64lf}
ACECTF{4ll_7h47_ju57_70_g37_f4k3_64lf}
ACECTF{4ll_7h47_ju57_70_g37_f4k3_64lf}
ACECTF{4ll_7h47_ju57_70_g37_f4k3_64lf}
ACECTF{4ll_7h47_ju57_70_g37_f4k3_64lf}
ACECTF{4ll_7h47_ju57_70_g37_f4k3_64lf}
</code></pre></div></div>

<p><br /></p>

<p>…And so on. Overall, 1830 lines of fake “galf”s and no real flag. Clearly, I need a different approach - and I hopefully have learned my lesson that if I get a bunch of fake flags while using <code class="language-plaintext highlighter-rouge">grep</code>, I probably have the wrong approach.</p>

<p><br /></p>

<h2 id="a-new-approach-and-a-bit-of-hindsight">A new approach, and a bit of hindsight</h2>

<p><br /></p>

<p>If you have ever worked with NTFS before, you may have heard of <a href="https://en.wikipedia.org/wiki/NTFS#Alternate_data_stream_(ADS)">Alternate Data Streams - ADS</a> - on Windows. The TLDR of it is that more than one data stream can be associated with one file, with the format <code class="language-plaintext highlighter-rouge">filename.extention:streamname</code>. This can hold metadata, malware, or… flags.</p>

<p><br /></p>

<p>On Linux, you can use <code class="language-plaintext highlighter-rouge">testdisk</code> to look at these alternate data streams. On selecting the mounted disk image, selecting <code class="language-plaintext highlighter-rouge">Advanced</code>, and then <code class="language-plaintext highlighter-rouge">List</code>, I can look at what files are on the disk - and what data streams exist on the files. Lo and behold…</p>

<p><img src="https://numberri.github.io/assets/images/harder-disk/success.png" alt="Image" class="post-img" /></p>

<p><br /></p>

<p>The file <code class="language-plaintext highlighter-rouge">666c61672e747874.jpg</code> has a flag and key! The data stream <code class="language-plaintext highlighter-rouge">666c61672e747874.jpg:Flag</code> has the string <code class="language-plaintext highlighter-rouge">CTCHHW{7t3_h1hw3p3sq3_s37i33r_a0l_4li_a3}</code>, and <code class="language-plaintext highlighter-rouge">666c61672e747874.jpg:Key</code> has the string <code class="language-plaintext highlighter-rouge">cryforme</code>. After this, it’s a simple Vigenère decode:</p>

<p><br /></p>

<p><img src="https://numberri.github.io/assets/images/harder-disk/got_the_flag.png" alt="Image" class="post-img" /></p>

<p><br /></p>

<p>This gets the flag: <code class="language-plaintext highlighter-rouge">ACECTF{7h3_d1ff3r3nc3_b37w33n_y0u_4nd_m3}</code></p>]]></content><author><name>numberri</name></author><category term="writeups," /><category term="acectf," /><category term="forensics" /><summary type="html"><![CDATA[Alternative tiltle for this one: How noob ways of thinking can shoot you in the back]]></summary></entry></feed>