두 달만의 포스팅이다. 한동안 CTF를 안하다가 POXX 2019 예선에 참가하게 되었다. 예선에서 내가 푼 문제를 정리 해 두려 한다.

magic2

문제에 접속하면 바로 코드가 보인다. 코드는 아래와 같다.

 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
<?php
include 'config.php';
show_source(__FILE__);
echo '<hr>';

$rflag = $flag;

if (!$_GET['a']) die('plz start');
if ($flag[0] != $_GET['a']) die('stage1');
if ($flag[strlen($flag)-1] != $_GET['b']) die('stage2');
if ($flag[1] == $_GET['c']) die('stage3');
if ($flag[0] == $_GET['a'] && 
    $flag[strlen($flag)-1] == $_GET['b'] &&
    $flag[strlen($flag)-3] == $_GET['c'])
    $flag = substr($flag, rand() % strlen($flag), rand() % strlen($flag));
else
    die('stage4');

if ($flag[0] != $_GET['A']) die('stage5');
if ($flag[strlen($flag)-1] != $_GET['B']) die('stage6<!--Hint: '.$flag);
if ($flag[1] == $_GET['C']) die('stage7');
if ($flag[0] == $_GET['A'] && 
    $flag[strlen($flag)-1] == $_GET['B'] &&
    $flag[strlen($flag)-3] == $_GET['C'])
    $flag = substr($flag, rand() % strlen($flag), rand() % strlen($flag));
else
    die('stage8');

if ($flag[0] != $_GET['q']) die('stage9');
if ($flag[strlen($flag)-1] != $_GET['w']) die('stage10<!--Hint: '.$flag);
if ($flag[1] == $_GET['e']) die('stage11');
if ($flag[0] == $_GET['q'] && 
    $flag[strlen($flag)-1] == $_GET['w'] &&
    $flag[strlen($flag)-3] == $_GET['e'])
    $flag = substr($flag, rand() % strlen($flag), rand() % strlen($flag));
else
    die('stage12');

if ($flag[0] != $_GET['Q']) die('stage13');
if ($flag[strlen($flag)-1] != $_GET['W']) die('stage14<!--Hint: '.$flag);
if ($flag[1] == $_GET['E']) die('stage15');
if ($flag[0] == $_GET['Q'] && 
    $flag[strlen($flag)-1] == $_GET['W'] &&
    $flag[strlen($flag)-3] == $_GET['E'])
    die("Flag: $rflag");
else
    die("final stage! but game is over, and you lose! :p<!-- $flag");

소스코드를 살펴보면 각 스테이지마다 $flag의 특정 위치의 값을 맞춰야 해당 스테이지를 통과할 수 있게 되어있다. 그런데 3~5개의 스테이지가 지날 때 마다 $flag의 값은 이 전의 $flag의 값에서 랜덤한 위치부터 랜덤한 갯수의 문자를 잘라 다시 $flag에 넣는다. 때문에 모든 스테이지를 통과하더라도 모든 $flag의 값을 읽을 수 없다. 그래서 그냥 중간에 보면 Hint라고 현재 $flag에 저장된 값을 출력 해 주는데, 여기에서 전체 flag의 값을 최대한 많이 출력 해 주길 바라면서 여러 번 실행시켰다. 소스코드는 아래와 같다.

 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
import requests

a = ""
length = 0

url = "http://167.179.71.234/pox/?"

print "[+] Start"

for i in range(48, 127):
        try:
                query = url + "a=" + chr(i)
                r = requests.post(query)
        except:
                print "[-] Error occur"
                continue
        if '</code><hr>stage2' in r.text and '</code><hr>plz start' not in r.text:
                a += chr(i)
b = ""
for i in range(48, 127):
        try:
                query = url + "a=" + a + "&b=" + chr(i)
                r = requests.post(query)
        except:
                continue
        if '</code><hr>stage2' not in r.text and '</code><hr>stage1' not in r.text:
                b += chr(i)

c_2 = ""
for i in range(48, 127):
        try:
                query = url + "a=" + a + "&b=" + b + "&c=" + chr(i)
                r = requests.post(query)
        except:
                continue
        if '</code><hr>stage3' in r.text:
                c_2 += chr(i)
c_3 = ""
for i in range(48, 127):
        try:
                query = url + "a=" + a + "&b=" + b + "&c=" + chr(i)
                r = requests.post(query) 
        except:
                continue
        if '</code><hr>stage5' in r.text:
                c_3 += chr(i)
hint = ""
A = ""
for i in range(48, 127):
        try:
                query = url + "a=" + a + "&b=" + b + "&c=" + c_3 + "&A=" + chr(i)
                r = requests.post(query)
        except:
                continue
        if '</code><hr>stage6' in r.text:
                hint = r.text.split('</code><hr>stage6<!--Hint: ')[1]
                print "[+] Hint :", hint
                A += chr(i)

print "[+] End"

이 코드를 실행 시킬 때 마다 플래그가 조금 씩 나타나고, 여러 번 실행 시켜서 조합하면 전체 플래그를 얻을 수 있다.

1
FLAG : POX{kong_wanted_to_make_problem_xD_flag_will_be_long_long?_wrong_:p}

Clue

이 문제에서는 파일 3개가 주어졌다.

  • clue.bas
  • clue.frm
  • clue.frx

그 중 clue.basclue.frm은 Excel로 열 수 있었는데, clue.frx는 안됬다. 그래서 뭔가 읽을 수 있는 문자를 찾을 수 있지 않을까 하면서 HxD로 열어 봤더니 아래와 같았다.

대부분은 알아볼 수 없었는데 읽을 수 있는 문자열을 찾다보니 아래의 두 문장을 찾을 수 있었다.

1
u5k624kcu_ngemct_suc_pdg_v_eibc
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
UHVibGljIEZ1bmN0aW9uIGdoaShCeVZhbCBqa2wgQXMgU3RyaW5nKSBBcyBTdHJpbmcgRGlt
IGljTGVuIEFzIEludGVnZXIgRGltIGljTmV3VGV4dCBBcyBTdHJpbmcgRGltIGFiYyBBcyBT
dHJpbmcgRGltIGkgQXMgSW50ZWdlciBhYmMgPSAiIiAgICBpY0xlbiA9IExlbihqa2wpICAg
IEZvciBpID0gMSBUbyBpY0xlbiAgICAgICAgYWJjID0gTWlkKGprbCwgaSwgMSkgICAgICAg
IFNlbGVjdCBDYXNlIEFzYyhhYmMpICAgICAgICAgICAgQ2FzZSA2NSBUbyA5MCAgICAgICAg
ICAgICAgICBhYmMgPSBDaHIoQXNjKGFiYykgWG9yIFVzZXJGb3JtMS5YT1IxLkNhcHRpb24p
ICAgICAgICAgICAgQ2FzZSA5NyBUbyAxMjIgICAgICAgICAgICAgICAgYWJjID0gQ2hyKEFz
YyhhYmMpIFhvciBVc2VyR m9 ybTEuWE9SMi5DYXB0aW9uKSAgICAgICAgICAgIENhc2UgNDgg
VG8gNTcgICAgICAgICAgICAgICAgYWJjID0gQ2hyKEFzYyhhYmMpIFhvciBVc2VyR m9 ybTEu
WE9SMy5DYXB0aW9uKSAgICAgICAgICAgIENhc2UgMzIgICAgICAgICAgICAgICAgYWJjID0g
Q2hyKDMyKSAgICAgICAgRW5kIFNlbGVjdCAgICAgICAgaWNOZXdUZXh0ID0gaWNOZXdUZXh0
ICsgYWJjICAgIE5leHQgICAgZ2hpID0gZGVmIEZ1bmN0aW9u

근데 아래 문장이 Base64 Encoding 된 값으로 보이길래 이를 Decoding 해 보았더니 아래의 코드를 얻을 수 있었다.

 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
Public Function ghi(ByVal jkl As String) As String
        Dim icLen As Integer
        Dim icNewText As String
        Dim abc As String
        Dim i As Integer

        abc = ""
        icLen = Len(jkl)
        For i = 1 To icLen
                abc = Mid(jkl, i, 1)


                Select Case Asc(abc)
                Case 65 To 90      // Capital letters
                        abc = Chr(Asc(abc) Xor UserForm1.XOR1.Caption)

                Case 97 To 122     // small letters
                        abc = Chr(Asc(abc) Xor UserForm1.XOR2.Caption)
                Case 48 To 57         // numbers
                        abc = Chr(Asc(abc) Xor UserForm1.XOR3.Caption)
                Case 32
                        abc = Chr(32)    // space bar
                End Select


                icNewText = icNewText + abc
        Next
        ghi = def Function

abc의 값을 UserForm1에 있는 XOR1, XOR2, XOR3의 캡션 값과 xor 연산을 하는 코드이다. 그런데 이 UserForm1Excel에서 열었던 clue.frm의 캡션 값이었다. 그래서 Excel을 통해 다른 개체들의 캡션을 찾아 보았다.

각각의 캡션 값을 확인하면 다음과 같다.

해당 값을 가지고 flag를 얻기 위해 아래처럼 코드를 짰다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
flag = "u5k624kcu_ngemct_suc_pdg_v_eibc"

xor1 = 7
xor2 = 6
xor3 = 5

FLAG = "POX{"

for i in range(len(flag)):
	char = flag[i]
	if ord(char) > 64 and ord(char) < 91:
		FLAG = FLAG + chr(ord(char) ^ xor1)
	elif ord(char) > 96 and ord(char) < 123:
		FLAG = FLAG + chr(ord(char) ^ xor2)
	elif ord(char) > 47 and ord(char) < 58:
		FLAG = FLAG + chr(ord(char) ^ xor3)
	elif ord(char) == 32:
		FLAG = FLAG + " "
	elif ord(char) == 95:
		FLAG = FLAG + "_"

위의 코드를 실행 한 결과 아래와 같이 flag를 얻을 수 있었다.

1
FLAG : POX{s0m371mes_hacker_use_vba_p_code}