Writeup: FlareOn 2020: 001 - Fidler

Task description

1. TLDR

Fidler graph

2. Input data

The challenge file is here. Password: flare.

The subject of the task is a program written in python. The available files are:

  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

Due to the fact that the program logic is implemented in the file fidler.py, it is enough to analyze this file.

3. Access to the program

Access to the program is protected: after starting, the user sees a screen where he is asked for a password.

Welcome screen

The program is written in python, its code is immediately available.

The password_check() function is responsible for verifying the password:

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

In order to obtain the password, it is enough to execute the script:

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

Thus, on the standard output, you can get the password written out, which allows you to access the program:

ghost

4. Code analysis

After entering the access password, the appropriate application window appears:

Main screen

The following functions are responsible for reading and presenting the flag:

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. Reading the flag

Therefore, in order to read the flag, the code should be executed:

#! /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()

The flag is printed on the standard output:

idle_with_kitty@flare-on.com