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")
과 같다는 뜻이다.
주소 배정은
위와 같으며
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 = [70, 108, 97, 103, 123, 83, 48, 109, 101, 48, 102, 85, 53] input1 = [103, 110, 105, 107, 48, 48, 76, 51, 114, 52] 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 |