본문 바로가기

CTF

[Codegate 2018] easy_serial

Codegate 2018 easy_serial reversing 924pt 


IDA로 보면 이런식으로 되게 복잡하게 생겼는데; 

C로 만들어진 바이너리가 아닌 것 같았다.

전에 삼성 CTF에서 IDA로 본 바이너리중에 비슷하게 복잡한 구조가 있었는데 해당 바이너리가 하스켈로 만들어졌었다.

하스켈로 컴파일된 바이너리인 걸 알고 바로 디컴파일을 시도하였다.

haskell decompiler (hsdecomp)

디컴파일 하던 도중에 오류가 있었는데 SHGroup님의 도움으로 고칠 수 있었다. (감사합니다)


[Decompile_source]

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
Main_main_closure=>> $fMonadIO
    (putStrLn (unpackCString# "Input Serial Key >>> "))
    (>>= $fMonadIO
        getLine
        (\s1dZ_info_arg_0 ->
            >> $fMonadIO
                (putStrLn (++ (unpackCString# "your serial key >>> ") (++ s1b7_info (++ (unpackCString# "_") (++ s1b9_info (++ (unpackCString# "_") s1bb_info))))))
                (case && (== $fEqInt (ord (!! s1b7_info loc_7172456)) (I# 70)) 
                (&& (== $fEqInt (ord (!! s1b7_info loc_7172472)) (I# 108)) 
                (&& (== $fEqInt (ord (!! s1b7_info loc_7172488)) (I# 97)) 
                (&& (== $fEqInt (ord (!! s1b7_info loc_7172504)) (I# 103)) 
                (&& (== $fEqInt (ord (!! s1b7_info loc_7172520)) (I# 123)) 
                (&& (== $fEqInt (ord (!! s1b7_info loc_7172536)) (I# 83)) 
                (&& (== $fEqInt (ord (!! s1b7_info loc_7172552)) (I# 48)) 
                (&& (== $fEqInt (ord (!! s1b7_info loc_7172568)) (I# 109)) 
                (&& (== $fEqInt (ord (!! s1b7_info loc_7172584)) (I# 101)) 
                (&& (== $fEqInt (ord (!! s1b7_info loc_7172600)) (I# 48)) 
                (&& (== $fEqInt (ord (!! s1b7_info (I# 10))) (I# 102)) 
                (&& (== $fEqInt (ord (!! s1b7_info (I# 11))) (I# 85)) 
                (== $fEqInt (ord (!! s1b7_info (I# 12))) (I# 53))))))))))))) of
                    
                    <tag 1> -> putStrLn (unpackCString# ":p"),
                    c1ni_info_case_tag_DEFAULT_arg_0@_DEFAULT -> 
                    case == ($fEq[] $fEqChar) (reverse s1b9_info) 
                    (: (C# 103) (: (C# 110) (: (C# 105) (: (C# 107) (: loc_7168872 (: loc_7168872 (: (C# 76) (: (C# 51) (: (C# 114) (: (C# 52) [])))))))))) of
                        False -> putStrLn (unpackCString# ":p"),
                        True -> case && (== $fEqChar (!! s1bb_info loc_7172456) (!! s1b3_info loc_7172456)) 
                                    (&& (== $fEqChar (!! s1bb_info loc_7172472) (!! s1b4_info (I# 19))) 
                                    (&& (== $fEqChar (!! s1bb_info loc_7172488) (!! s1b3_info (I# 19))) 
                                    (&& (== $fEqChar (!! s1bb_info loc_7172504) (!! s1b4_info loc_7172568)) 
                                    (&& (== $fEqChar (!! s1bb_info loc_7172520) (!! s1b2_info loc_7172488)) 
                                    (&& (== $fEqChar (!! s1bb_info loc_7172536) (!! s1b3_info (I# 18))) 
                                    (&& (== $fEqChar (!! s1bb_info loc_7172552) (!! s1b4_info (I# 19))) 
                                    (&& (== $fEqChar (!! s1bb_info loc_7172568) (!! s1b2_info loc_7172504)) 
                                    (&& (== $fEqChar (!! s1bb_info loc_7172584) (!! s1b4_info (I# 17))) 
                                    (== $fEqChar (!! s1bb_info loc_7172600) (!! s1b4_info (I# 18))))))))))) of
                            <tag 1> -> putStrLn (unpackCString# ":p"),
                            c1tb_info_case_tag_DEFAULT_arg_0@_DEFAULT -> putStrLn (unpackCString# "Correct Serial Key! Auth Flag!")
                )
        )
    )
loc_7172456=I# 0
loc_7172472=I# 1
loc_7172488=I# 2
loc_7172504=I# 3
loc_7172520=I# 4
loc_7172536=I# 5
loc_7172552=I# 6
loc_7172568=I# 7
loc_7172584=I# 8
loc_7172600=I# 9
loc_7168872=C# 48
s1b2_info = unpackCString# "1234567890"
s1b3_info = unpackCString# "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
s1b4_info = unpackCString# "abcdefghijklmnopqrstuvwxyz"
s1b5_info = splitOn $fEqChar (unpackCString# "#") s1dZ_info_arg_0
s1b7_info=!! s1b5_info loc_7172456
s1b9_info=!! s1b5_info loc_7172472
s1bb_info=!! s1b5_info loc_7172488
 
cs

보기좋게 정리한 소스이다.

하스칼 언어는 잘 몰라서 짐작으로 파이썬처럼 생각해 풀었다.

일단 위 소스를 분석한 결과는 아래와 같다.


1. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
loc_7172456=I# 0
loc_7172472=I# 1
loc_7172488=I# 2
loc_7172504=I# 3
loc_7172520=I# 4
loc_7172536=I# 5
loc_7172552=I# 6
loc_7172568=I# 7
loc_7172584=I# 8
loc_7172600=I# 9
loc_7168872=C# 48
s1b2_info = unpackCString# "1234567890"
s1b3_info = unpackCString# "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
s1b4_info = unpackCString# "abcdefghijklmnopqrstuvwxyz"
s1b5_info = splitOn $fEqChar (unpackCString# "#") s1dZ_info_arg_0
s1b7_info=!! s1b5_info loc_7172456
s1b9_info=!! s1b5_info loc_7172472
s1bb_info=!! s1b5_info loc_7172488
cs

위 15번째 코드를 보면 fEqChar을 input으로 추측했고 #을 기준으로 split 한다는 코드 같다.

즉 파이썬으로 따지면 input = input.split('#') 같은 의미다.

위에 case 비교하는 부분을 보면 s1b7_info, s1b9_info, s1bb_info 3개의 배열들과 비교를 한다.

그 말은 입력할 문자열에서 #의 개수는 2개란 뜻이다.

파이썬으로 비유해보자면..

input = "aaa#bbb#ccc" 

input = input.split('#') 에서

input[0] = s1b7_info ("aaa")

input[1] = s1b9_info ("bbb")

input[2] = s1bb_info ("ccc")

과 같다는 뜻이다.


주소 배정은 

loc_7172456=I# 0
loc_7172472=I# 1
loc_7172488=I# 2
loc_7172504=I# 3
loc_7172520=I# 4
loc_7172536=I# 5
loc_7172552=I# 6
loc_7172568=I# 7
loc_7172584=I# 8
loc_7172600=I# 9

위와 같으며 

input[0][0] == s1b7_info (I# 0) == loc_7172456

input[1][0] == s1b9_info (I# 0) == loc_7172456

input[2][0] == s1bb_info (I# 0) == loc_7172456

각 배열마다 같은 주소를 가지게 된다. (10글자씩 각 0x12) 

(옆에 있는 I# 0  뜻은 [0] 라고 생각하면 된다.)


2.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Main_main_closure=>> $fMonadIO
    (putStrLn (unpackCString# "Input Serial Key >>> "))
    (>>= $fMonadIO
        getLine
        (\s1dZ_info_arg_0 ->
            >> $fMonadIO
                (putStrLn (++ (unpackCString# "your serial key >>> ") (++ s1b7_info (++ (unpackCString# "_") (++ s1b9_info (++ (unpackCString# "_") s1bb_info))))))
                (case && (== $fEqInt (ord (!! s1b7_info loc_7172456)) (I# 70)) 
                (&& (== $fEqInt (ord (!! s1b7_info loc_7172472)) (I# 108)) 
                (&& (== $fEqInt (ord (!! s1b7_info loc_7172488)) (I# 97)) 
                (&& (== $fEqInt (ord (!! s1b7_info loc_7172504)) (I# 103)) 
                (&& (== $fEqInt (ord (!! s1b7_info loc_7172520)) (I# 123)) 
                (&& (== $fEqInt (ord (!! s1b7_info loc_7172536)) (I# 83)) 
                (&& (== $fEqInt (ord (!! s1b7_info loc_7172552)) (I# 48)) 
                (&& (== $fEqInt (ord (!! s1b7_info loc_7172568)) (I# 109)) 
                (&& (== $fEqInt (ord (!! s1b7_info loc_7172584)) (I# 101)) 
                (&& (== $fEqInt (ord (!! s1b7_info loc_7172600)) (I# 48)) 
                (&& (== $fEqInt (ord (!! s1b7_info (I# 10))) (I# 102)) 
                (&& (== $fEqInt (ord (!! s1b7_info (I# 11))) (I# 85)) 
                (== $fEqInt (ord (!! s1b7_info (I# 12))) (I# 53))))))))))))) of
                    
                    <tag 1> -> putStrLn (unpackCString# ":p")
cs

위 코드를 보면 일단 getLine으로 serial Key를 입력받는 것 같다.

후에 s1b7_info + '_' + s1b9_info + '_' + s1bb_info 을 출력해주는데 문자열 '#' 을 기준으로 split 했기 때문에 구분해주는 것이다.

오히려 입력값에 # 두개가 없으면 오류가 뜬다

그 다음에 이제 #을 기준으로 split한 input[0] 배열들과 비교를 하는데 

s1b7_info loc_7172456 는 input[0][0] 을 나타낸다.

s1b7_info loc_7172472 는 input[0][1] 을 나타낸다.

즉 각 배열들을 옆에 있는 ascii 값들과 한글자씩 비교한다.

input[0] = [70, 108, 97, 103, 123, 83, 48, 109, 101, 48, 102, 85, 53] 

input[0] 배열이 위와 같지 않다면 :p 을 출력한다.


3.

1
2
3
case == ($fEq[] $fEqChar) (reverse s1b9_info) 
(: (C# 103) (: (C# 110) (: (C# 105) (: (C# 107) (: loc_7168872 (: loc_7168872 (: (C# 76) (: (C# 51) (: (C# 114) (: (C# 52) [])))))))))) of
    False -> putStrLn (unpackCString# ":p")
cs

위 코드를 보면 s1b9_info(input[1]) 배열을 reverse 즉 거꾸로 뒤집은 후 아래 값들과 비교를 한다.

input[1] = [103, 110, 105, 107, 48, 48, 76, 51, 114, 52] (loc_7168872 = C# 48)

input[1] = input[1][::-1]


4.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 True -> case && (== $fEqChar (!! s1bb_info loc_7172456) (!! s1b3_info loc_7172456)) 's1b3_info (I# 0)
             (&& (== $fEqChar (!! s1bb_info loc_7172472) (!! s1b4_info (I# 19))) 
             (&& (== $fEqChar (!! s1bb_info loc_7172488) (!! s1b3_info (I# 19))) 
             (&& (== $fEqChar (!! s1bb_info loc_7172504) (!! s1b4_info loc_7172568)) 's1b4_info (I# 7)
             (&& (== $fEqChar (!! s1bb_info loc_7172520) (!! s1b2_info loc_7172488)) 's1b2_info (I# 2)
             (&& (== $fEqChar (!! s1bb_info loc_7172536) (!! s1b3_info (I# 18))) 
             (&& (== $fEqChar (!! s1bb_info loc_7172552) (!! s1b4_info (I# 19))) 
             (&& (== $fEqChar (!! s1bb_info loc_7172568) (!! s1b2_info loc_7172504)) 's1b2_info (I# 3) 
             (&& (== $fEqChar (!! s1bb_info loc_7172584) (!! s1b4_info (I# 17))) 
             (== $fEqChar (!! s1bb_info loc_7172600) (!! s1b4_info (I# 18))))))))))) of
        <tag 1> -> putStrLn (unpackCString# ":p"),
        c1tb_info_case_tag_DEFAULT_arg_0@_DEFAULT -> putStrLn (unpackCString# "Correct Serial Key! Auth Flag!")
's1b2_info = unpackCString# "1234567890"
's1b3_info = unpackCString# "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
's1b4_info = unpackCString# "abcdefghijklmnopqrstuvwxyz"
cs

위에서 설명했던 부분을 이해했다면 지금 이 코드가 이해가 갈것이다.

input[2] = ['A', 't', 'T', 'h', '3', 'S', 't', '4', 'r', 's']

이제 이어 붙여보자.

[Solve]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
input0 = [70108971031238348109101481028553]
input1 = [1031101051074848765111452]
input1 = input1[::-1]
input2 = ['A''t''T''h''3''S''t''4''r''s']
flag = ''
 
for i in input0:
    flag += chr(i)
flag += '#'
 
for i in input1:
    flag += chr(i)
flag += '#'
 
for i in input2:
    flag += i
 
print flag #Flag{S0me0fU5#4r3L00king#AtTh3St4rs
cs


1
2
3
4
5
pwnr00t@System:~/gyeongje$ ./easy 
Input Serial Key >>> 
Flag{S0me0fU5#4r3L00king#AtTh3St4rs
your serial key >>> Flag{S0me0fU5_4r3L00king_AtTh3St4rs
Correct Serial Key! Auth Flag!
cs


'CTF' 카테고리의 다른 글

[Samsung CTF 2017] Easyhaskell  (0) 2018.02.05
[Codegate 2018] BaskinRobins31  (0) 2018.02.05
[Codegate 2018] RedVelvet  (0) 2018.02.05
[Codegate 2018] Impel Down  (0) 2018.02.04
[AceBear Security Contest] secure login  (0) 2018.02.02