Codegate 2018 Prequal easy_serial
개요
easy_serial - 924pts (Rev)
Solve 2
이곳
(Junior 으로 참가하여서 일반부와는 문제의 점수가 다를 수 있습니다.)
풀이
바이너리를 분석을 위해 IDA 를 사용하였다.
리눅스 64비트 바이너리 파일이였다.
함수가 엄청 많았는데, 분석을 하다보니 하스켈로 짜여진 바이너리라는 것을 알게 되었다.
왜냐하면
몇몇 함수 이름들이 hs_ 로 시작했었고, hs_main 함수가 있었기 때문이다.
하스켈 디컴파일러가 있지 않을까 하는 마음에 깃헙에서 찾아봤는데, 있긴 있었다.
hsdecomp (Jonathan S 님에게 감사드립니다. ^-^)
먼저 이를 사용하려고 클론을 하고, python ./runner.py ../easy 를 입력했는데, 오류가 났다..
하지만 어느정도 동작은 하는 것을 발견하였고, 오류가 나는 부분을 고치기로 하였다.
오류나는 부분을 찾아 들어가면서 오류를 고치기 위해 조금의 삽질을 하였다.
이곳
이게 하스켈 디컴파일러의 출력이다.
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!")
)
)
)
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
바로 보기는 힘들어 보여서 조금 더 보기 쉽게 고쳤다.
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 I# 0)) (I# 70)
(&& (== $fEqInt (ord (!! s1b7_info I# 1)) (I# 108))
(&& (== $fEqInt (ord (!! s1b7_info I# 2)) (I# 97))
(&& (== $fEqInt (ord (!! s1b7_info I# 3)) (I# 103))
(&& (== $fEqInt (ord (!! s1b7_info I# 4)) (I# 123))
(&& (== $fEqInt (ord (!! s1b7_info I# 5)) (I# 83))
(&& (== $fEqInt (ord (!! s1b7_info I# 6)) (I# 48))
(&& (== $fEqInt (ord (!! s1b7_info I# 7)) (I# 109))
(&& (== $fEqInt (ord (!! s1b7_info I# 8)) (I# 101))
(&& (== $fEqInt (ord (!! s1b7_info I# 9)) (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) (: C# 48 (: C# 48 (: (C# 76) (: (C# 51) (: (C# 114) (: (C# 52) []))))))))))
of False -> putStrLn (unpackCString# ":p"), True -> case
&& (== $fEqChar (!! s1bb_info I# 0) (!! uppercase_string I# 0))
(&& (== $fEqChar (!! s1bb_info I# 1) (!! lowercase_string (I# 19)))
(&& (== $fEqChar (!! s1bb_info I# 2) (!! uppercase_string (I# 19)))
(&& (== $fEqChar (!! s1bb_info I# 3) (!! lowercase_string I# 7))
(&& (== $fEqChar (!! s1bb_info I# 4) (!! number_string I# 2))
(&& (== $fEqChar (!! s1bb_info I# 5) (!! uppercase_string (I# 18)))
(&& (== $fEqChar (!! s1bb_info I# 6) (!! lowercase_string (I# 19)))
(&& (== $fEqChar (!! s1bb_info I# 7) (!! number_string I# 3))
(&& (== $fEqChar (!! s1bb_info I# 8) (!! lowercase_string (I# 17)))
(== $fEqChar (!! s1bb_info I# 9) (!! lowercase_string (I# 18)))))))))))
of <tag 1> -> putStrLn (unpackCString# ":p"),
c1tb_info_case_tag_DEFAULT_arg_0@_DEFAULT -> putStrLn (unpackCString# "Correct Serial Key! Auth Flag!")
)
)
)
lowercase_string=unpackCString# "abcdefghijklmnopqrstuvwxyz"
number_string=unpackCString# "1234567890"
uppercase_string=unpackCString# "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
s1b5_info=splitOn $fEqChar (unpackCString# "#") s1dZ_info_arg_0
s1bb_info=!! s1b5_info I# 2
s1b9_info=!! s1b5_info I# 1
s1b7_info=!! s1b5_info I# 0
소스를 보니 문자열을 입력받고, # 를 기준으로 문자열 세 가지로 나눈다.
그 이후 다음과 같이 세 번 검사한다.
- 나눈 문자열 중 첫 번째 문자열은 미리 정해둔 값들과 같은지 검사한다.
- 나눈 문자열 중 두 번째 문자열은 거꾸로 뒤집어서 미리 정해둔 값들과 검사한다.
- 나눈 문자열 중 마지막 문자열은 미리 정의된 몇 가지 배열에서 n번째 값과 검사한다.
- 미리 정의된 배열은 3가지 이다.
- 소문자 문자열 배열 :
abcdefghijklmnopqrstuvwxyz - 대문자 문자열 배열 :
ABCDEFGHIJKLMNOPQRSTUVWXYZ - 숫자 배열 :
1234567890
이제 이것을 통과할 수 있는 값을 얻기 위해 다음과 같은 파이썬 코드를 작성하였다.
def chrs(cs):
n = ""
for c in cs:
n += chr(c)
return n
def num(a):
return (chr(a + ord('1')))
def lo(a):
return (chr(a + ord('a')))
def up(a):
return (chr(a + ord('A')))
flag = ""
flag += chrs((70, 108, 97, 103, 123, 83, 48, 109, 101, 48, 102, 85, 53))
flag += "#"
flag += chrs((52, 114, 51, 76, 48, 48, 107, 105, 110, 103))
flag += "#"
flag += up(0) + lo(19) + up(19) + lo(7) + num(2) + up(18) + lo(19) + num(3) + lo(17) + lo(18)
print(flag)
그리고, 최종적으로 이 소스코드의 출력값은 다음과 같았다.
행사가 다가올수록 캐릭터의 정확성 관련 세부 사항이 전체적인 모습에 어떤 영향을 미칠지 고려하는 것이 좋습니다. 최종 사진 촬영 시에는 원신 코스프레 의상의 디테일과 관리 및 보관 필요성을 균형 있게 고려하는 것이 중요합니다. 축제 의상과 관련된 디테일을 중심으로 완벽한 모습을 완성하려면 배송 정보가 포함된 코스프레 의상 세트를 통해 특정 목적에 맞는 선택지를 좁힐 수 있습니다. 준비를 마치기 전에 초보자용 의상 선택과 관련된 디테일을 균형 있게 살펴보면 디테일과 편안함 모두를 확보할 수 있습니다.
Flag{S0me0fU5#4r3L00king#AtTh3St4rs
(}는 왜 없지.... ㅇㅁㅇ)
Flag
S0me0fU5#4r3L00king#AtTh3St4rs