본문 바로가기

[Wargame Write-up]/TryThis0ne

[TryThisOne] [Application] KeyGenMe1

Keygen 문제인데, User에 입력할 값은 주어져 있다.




Form은 이렇게 생겼다. 총 5개의 Text Field를 채워야 한다.




예를 들어 아래와 같이 입력된 경우 오른편의 메시지 박스가 나타난다.


            



PEiD로 확인했더니 C#이다. 웬만해선 쉽게 풀 수 있을 것 같다.




Button1_Click이라는 이벤트 처리 메소드에서 Checkyn이라는 메소드 결과를 이용해 분기 작업을 수행한다.




Checkyn 메소드의 내용은 다음과 같다.


처음에 각 입력값에 대한 길이가 15를 넘지 않으면 X라는 문자로 패딩작업을 한다.


그렇게 16자리 문자열을 만들어낸 후, 일종의 key값인 A!<>FWRSGT#$324A를 오름차순 정렬한 문자열을 이용해 XOR, modular 연산을 거친 결과를 누적시켜 수를 만들어낸다.


두 입력값의 상기 설명한 연산 결과의 차이가 3이 되어야 결과로 True를 볼 수 있다.


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
private bool Checkyn(string user, string serial)
{
    int length1 = user.Length;
    while (length1 <= 15)
    {
        user += "X";
        checked { ++length1; }
    }
    int length2 = serial.Length;
    while (length2 <= 15)
    {
        serial += "X";
        checked { ++length2; }
    }
    string str = "A!<>FWRSGT#$324A";
    int num1 = 0;
    int num2 = 0;
    int num3 = checked (str.Length - 1);
    int index1 = num2;
    while (index1 <= num3)
    {
        checked { num1 += Strings.Asc(str[index1]) ^ unchecked (Strings.Asc(user[index1]) % checked (index1 + 1)); }
        checked { ++index1; }
    }
    int num4 = checked (num1 + 3);
    int num5 = 0;
    int num6 = 0;
    int num7 = checked (str.Length - 1);
    int index2 = num6;
    while (index2 <= num7)
    {
        checked { num5 += Strings.Asc(str[index2]) ^ unchecked (Strings.Asc(serial[index2]) % checked (index2 + 1)); }
        checked { ++index2; }
    }
    return num4 == num5;
}
cs



Checkyn을 Python에서 그대로 구현한 후, brute forcing을 위한 코드도 추가하였다.


나름 선형화하여 처음에는 X가 15개일 때부터 즉, 총 62개(대소문자+숫자)의 입력 가능 경우의 수 중 답이 있는지 확인한다.


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
#-*- coding: utf-8 -*-
import os
import string
 
def Checkyn(user, serial=None):
    length1 = len(user)
    while length1 <= 15:
        user += "X"
        length1 += 1
 
    length2 = len(serial)
    while length2 <= 15:
        serial += "X"
        length2 += 1
 
    str = "A!<>FWRSGT#$324A"
    num1 = index1 = 0
    num3 = len(str- 1
    while index1 <= num3:
        num1 += ord(''.join(sorted(list(str)))[index1]) ^ ord(''.join(sorted(list(user)))[index1]) % (index1 + 1)
        print "(str[i], user[i], str[i]^user[i], i) -->",
        print (ord(''.join(sorted(list(str)))[index1]),  ord(''.join(sorted(list(user)))[index1]), ord(''.join(sorted(list(str)))[index1]) ^ ord(''.join(sorted(list(user)))[index1]), (index1 + 1), num1)
        index1 += 1
 
    num5 = index2 = 0
    num7 = len(str- 1
    while index2 <= num7:
        num5 += ord(''.join(sorted(list(str)))[index2]) ^ ord(''.join(sorted(list(serial)))[index2]) % (index2 + 1)
        print "(str[i], serial[i], str[i]^serial[i], i) -->",
        print (ord(''.join(sorted(list(str)))[index2]), ord(''.join(sorted(list(serial)))[index2]), ord(''.join(sorted(list(str)))[index2]) ^ ord(''.join(sorted(list(serial)))[index2]), (index2 + 1), num5)
        index2 += 1
 
    return (num1 + 3), num5, (num1 + 3== num5
 
def brute_force_serial_for_checkyn(user):    # Padding 문자인 X가 15개일 때부터 시도, 하나도 없으면 점점 X의 수를 감소시킴
    letters = string.letters + string.digits
    
    for char in letters:
            if Checkyn(user, "X"*15 + char)[2== True:
                print Checkyn(user, "X"*15 + char)
                break
 
    return "XXXX-"*3 + "XXX" + char
  
def main():
    os.system("cls")
    # print Checkyn("Trythis0ne", "X"*15+"b")
    print "\nAnswer: " + brute_force_serial_for_checkyn("Trythis0ne")
 
if __name__ == '__main__':
    main()
cs



결과는 다음과 같이 나타난다.


답을 찾을 경우 마지막에 Answer: 이후에 나타난다.




X 15개일 때 답이 하나 나왔으므로, 맞는지 확인해보면 맞다는 것을 알 수 있다.


어차피 정렬한 후 연산하므로 16글자의 순서는 상관없을 것 같았는데, 실제 순서를 바꿔 입력해 보면, 성공 메시지가 나타나지 않는다.


C#의 Strings.Asc는 Python의 sorted와 달리 ASCII 코드를 기준으로 하는 것이 아니라 말그대로 대소문자 상관없이 알파벳 순으로 정렬하는 것 같다. 


즉, XXXXXXXXXXXXXXXA를 정렬한 것과 XXXXXXXXXXXXXXXa를 정렬한 것이 모두 A나 a를 맨 앞으로 보낸 결과가 될 것이라는 추측을 해 본다.


            



답이 여럿 나올 수 있겠지만, 일단 하나 건졌으니 입력해보자.




인증키를 얻을 수 있다.