Codegate 2018 Prequal easy_serial

개요

easy_serial - 924pts (Rev)
Solve 2

Download

이곳

(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

소스를 보니 문자열을 입력받고, # 를 기준으로 문자열 세 가지로 나눈다.

그 이후 다음과 같이 세 번 검사한다.

  1. 나눈 문자열 중 첫 번째 문자열은 미리 정해둔 값들과 같은지 검사한다.
  2. 나눈 문자열 중 두 번째 문자열은 거꾸로 뒤집어서 미리 정해둔 값들과 검사한다.
  3. 나눈 문자열 중 마지막 문자열은 미리 정의된 몇 가지 배열에서 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