[2018_Codegate_CTF] Easy_Serial (350)

Updated:

Exercise

Download

너무 매정한 지문..

[그림] exercise

Solution

다운받은 binary를 확인해보면, ELFx64임을 알 수 있다.

 ⚡ root@RebForPwn  ~/jsh  file ./easy
./easy: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=4bd1814b358aaa9e61a968f20bd14ffa9cd208e8, not stripped


실행해보면, serial Key를 입력 받는 것을 확인해 볼 수 있다.

[그림] run

IDA로 열어보면 hs_main부분을 확인할 수 있는데, Haskell로 작성된 binary 이다.

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // r8
  __int64 v4; // r9
  __int64 v5; // ST30_8

  HIDWORD(v5) = HIDWORD(defaultRtsConfig);
  LODWORD(v5) = 1;
  hs_main(argc, (__int64)argv, (__int64)&ZCMain_main_closure, (__int64)argv, v3, v4, v5, 0LL, 1LL);
}


예전에 Samsung CTF 본선에서 하스켈로 짜여진 문제가 나왔던 기억이 있는데, 그때 당시에도 decompiler를 찾아보려 했었지만, 딱히 정상적으로 변환이 되는 항목은 볼 수 없었다. (테이블 만들고, 브포하고 노가다 했던 기억이…) 근데, git hub 에서 다시 찾아본 결과 “https://github.com/gereeter/hsdecomp” 에서의 decompiler를 이용해 Binary –> Code 형태로 변환할 수 있다. 그대로 디컴파일 시도할 경우, 에러가 나는데 팀원이 에러나는 부분을 수정해서 제공해줬다~

변환한 Haskell Code는 아래와 같다.

('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 0) (!! s1b3_info loc_7172456)) (&&
                            (== $fEqChar (!! s1bb_info 1) (!! s1b4_info (I# 19))) (&&
                                (== $fEqChar (!! s1bb_info 2) (!! s1b3_info (I# 19))) (&&
                                    (== $fEqChar (!! s1bb_info 3) (!! s1b4_info 7)) (&&
                                        (== $fEqChar (!! s1bb_info 4) (!! s1b2_info 2)) (&&
                                            (== $fEqChar (!! s1bb_info 5) (!! s1b3_info (I# 18))) (&&
                                                (== $fEqChar (!! s1bb_info 6) (!! s1b4_info (I# 19))) (&&
                                                    (== $fEqChar (!! s1bb_info 7) (!! s1b2_info 3)) (&&
                                                        (== $fEqChar (!! s1bb_info 8) (!! s1b4_info (I# 17)))
                                                            (== $fEqChar (!! s1bb_info 9) (!! 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!")
                )
        )
    )')
('s1b4_info', '=', 'unpackCString# "abcdefghijklmnopqrstuvwxyz"')
('loc_7172600', '=', 'I# 9')
('s1bb_info', '=', '!! s1b5_info loc_7172488')
('loc_7172488', '=', 'I# 2')
('s1b5_info', '=', 'splitOn $fEqChar (unpackCString# "#") s1dZ_info_arg_0')
('loc_7172584', '=', 'I# 8')
('loc_7172504', '=', 'I# 3')
('s1b2_info', '=', 'unpackCString# "1234567890"')
('loc_7172568', '=', 'I# 7')
('loc_7172552', '=', 'I# 6')
('s1b3_info', '=', 'unpackCString# "ABCDEFGHIJKLMNOPQRSTUVWXYZ"')
('loc_7172536', '=', 'I# 5')
('loc_7172520', '=', 'I# 4')
('loc_7172472', '=', 'I# 1')
('loc_7172456', '=', 'I# 0')
('loc_7168872', '=', 'C# 48')
('s1b9_info', '=', '!! s1b5_info loc_7172472')
('s1b7_info', '=', '!! s1b5_info loc_7172456')

문법을 조금 찾아보면, 입력 값에 대해서 #으로 구분하여 3부분으로 나눈다. ASCII 값 비교 구문을 통해, serial check를 수행하는데, 첫번째 구간에 해당하는 string은 아래와 같다.

(== $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)))))))))))))
>>> a = [70,108,97,103,123,83,48,109,101,48,102,85,53]
>>> for i in a:
...     print chr(i),
...
F l a g { S 0 m e 0 f U 5


두번째 구간에 해당 하는 비교 부분은 아래와 같다.

(: (C# 103)
    (: (C# 110)
        (: (C# 105)
            (: (C# 107)
                (: loc_7168872
                    (: loc_7168872
                        (: (C# 76)
                            (: (C# 51)
                                (: (C# 114)
                                    (: (C# 52) [])))))))))) of
>>> b = [103,110,105,107,48,48,76,51,114,52]
>>> for i in b:
...     print chr(i),
...
g n i k 0 0 L 3 r 4
>>> r = "g n i k 0 0 L 3 r 4"
>>> r[::-1]
'4 r 3 L 0 0 k i n g'

세번째 구간에 해당하는 비교 부분은 아래와 같다.

(== $fEqChar (!! s1bb_info 0) (!! s1b3_info loc_7172456)) (&&
    (== $fEqChar (!! s1bb_info 1) (!! s1b4_info (I# 19))) (&&
        (== $fEqChar (!! s1bb_info 2) (!! s1b3_info (I# 19))) (&&
            (== $fEqChar (!! s1bb_info 3) (!! s1b4_info 7)) (&&
                (== $fEqChar (!! s1bb_info 4) (!! s1b2_info 2)) (&&
                    (== $fEqChar (!! s1bb_info 5) (!! s1b3_info (I# 18))) (&&
                        (== $fEqChar (!! s1bb_info 6) (!! s1b4_info (I# 19))) (&&
                            (== $fEqChar (!! s1bb_info 7) (!! s1b2_info 3)) (&&
                                (== $fEqChar (!! s1bb_info 8) (!! s1b4_info (I# 17)))
                                    (== $fEqChar (!! s1bb_info 9) (!! 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!"))
>>> s1b3_info = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
>>> s1b2_info = "1234567890"
>>> s1b4_info = "abcdefghijklmnopqrstuvwxyz"
>>> s1b3_info[0]
'A'
>>> s1b4_info[19]
't'
>>> s1b3_info[19]
'T'
>>> s1b4_info[7]
'h'
>>> s1b2_info[2]
'3'
>>> s1b3_info[18]
'S'
>>> s1b4_info[19]
't'
>>> s1b2_info[3]
'4'
>>> s1b4_info[17]
'r'
>>> s1b4_info[18]
's'
>>> AtTh3St4rs


각 구간별, string을 합치면 Flag{S0me0fU5#4r3L00king#AtTh3St4rs임을 알 수 있으며, input serial로 입력할 경우 Flag를 확인할 수 있다.

[그림] result

Flag{S0me0fU5_4r3L00king_AtTh3St4rs}

Leave a comment