Writeup: FlareOn 2020: 001 - Fidler

Task description

1. TLDR

Fidler graph

2. Dane wejściowe

Plik z zadaniem znajduje się tutaj. Hasło: flare.

Przedmiotem zadania jest program napisany w pythonie. Dostępne pliki to:

  controls.py
  fidler.py
  fonts/arial.ttf
  fonts/courbd.ttf
  img/btndown.png
  img/btnup.png
  img/clock.png
  img/coin.png
  img/fbi.png
  img/kittyelaine.png
  Message.txt
  fidler.exe

Ze względu na fakt, że implementacja logiki programu znajduje się w pliku fidler.py, wystarczy ograniczyć się do analizy tego pliku.

3. Dostęp do programu

Dostęp do programu jest chroniony: po uruchomieniu użytkownik widzi ekran, na którym jest proszony o hasło.

Welcome screen

Program został napisany w pythonie, jego kod jest od razu dostępny.

Za weryfikację hasła odpowiada funkcja password_check():

def password_check(input):
    altered_key = 'hiptu'
    key = ''.join([chr(ord(x) - 1) for x in altered_key])
    return input == key

W celu pozyskania hasła wystarczy więc wykonać skrypt:

altered_key = 'hiptu'
key = ''.join([chr(ord(x) - 1) for x in altered_key])
print(key)

W ten sposób na standardowym wyjściu można otrzymać wypisane hasło, które pozwala uzyskać dostęp do programu:

ghost

4. Analiza kodu

Po wpisaniu hasła dostępowego ukazuje się właściwe okno aplikacji:

Main screen

Za odczytanie i prezentację flagi odpowiadają funkcje:

def decode_flag(frob):
    last_value = frob
    encoded_flag = [1135, 1038, 1126, 1028, 1117, 1071, 1094, 1077, 1121, 1087, 1110, 1092, 1072, 1095, 1090, 1027,
                    1127, 1040, 1137, 1030, 1127, 1099, 1062, 1101, 1123, 1027, 1136, 1054]
    decoded_flag = []

    for i in range(len(encoded_flag)):
        c = encoded_flag[i]
        val = (c - ((i%2)*1 + (i%3)*2)) ^ last_value
        decoded_flag.append(val)
        last_value = c

    return ''.join([chr(x) for x in decoded_flag])
def victory_screen(token):
    ...
    flag_content_label.change_text(decode_flag(token))
    ...
def game_screen():
    ...
    while not done:
        target_amount = (2**36) + (2**35)
        if current_coins > (target_amount - 2**20):
            while current_coins >= (target_amount + 2**20):
                current_coins -= 2**20
            victory_screen(int(current_coins / 10**8))
            return
    ...

5. Odczytanie flagi

W celu odczytania flagi należy zatem wykonać kod:

#! /usr/bin/python3

def decode_flag(frob):
    last_value = frob
    encoded_flag = [1135, 1038, 1126, 1028, 1117, 1071, 1094, 1077, 1121, 1087, 1110, 1092, 1072, 1095, 1090, 1027,
                    1127, 1040, 1137, 1030, 1127, 1099, 1062, 1101, 1123, 1027, 1136, 1054]
    decoded_flag = []

    for i in range(len(encoded_flag)):
        c = encoded_flag[i]
        val = (c - ((i%2)*1 + (i%3)*2)) ^ last_value
        decoded_flag.append(val)
        last_value = c

    return ''.join([chr(x) for x in decoded_flag])

def victory_screen(token):
    print(decode_flag(token))

def main():
    target_amount = (2**36) + (2**35)
    current_coins = (target_amount - 2**20) + 1
    while current_coins >= (target_amount + 2**20):
        current_coins -= 2**20
    victory_screen(int(current_coins / 10**8))

if __name__ == "__main__":
    main()

W ten sposób na standardowym wyjściu zostaje wypisana flaga:

idle_with_kitty@flare-on.com