TheLeopard65
Published on

BroncoCTF 2025 Challenges Writeup

AUTHORS
  • avatar
    NAME
    Yasir Mehmood
    TWITTER

REVERSE ENGINEERING


1. Reversing for Ophidiophiles


Reversing-for-Ophidiophiles.png

  1. The provided us with a simple chall.py python file. Here are the contents of the challenge file:

flag = input()
carry = 0
key = "Awesome!"
output = []
for i,c in enumerate(flag):
    val = ord(c)
    val += carry
    val %= 256
    val ^= ord(key[i % len(key)])
    output.append(val)
    carry += ord(c)
    carry %= 256
print(bytes(output).hex())

  1. After analyzing the file for a bit. I wrote the following script to solve this challenge:

output_hex = "23a326c27bee9b40885df97007aa4dbe410e93"
encrypted_bytes = bytes.fromhex(output_hex)
key = "Awesome!"
reversed_flag = []
carry = 0

for i in range(len(encrypted_bytes)):
    encrypted_byte = encrypted_bytes[i]
    key_char = key[i % len(key)]
    key_val = ord(key_char)
    val = encrypted_byte ^ key_val
    val = (val - carry) % 256
    reversed_flag.append(chr(val))
    carry = (carry + val) % 256

reversed_flag = ''.join(reversed(reversed_flag))
final_flag = reversed_flag[::-1]
print(final_flag)

  1. Here is the flag:
bronco{charge_away}

2. Theflagishere!


theflagishere.png

  1. The provided with a python bytecode file which was neither being executed nor being analyzed in my WSL2 Kali Linux. Here are the details of the file:
(kali@LEOPARD-PC)-[~/CTF-Events/BroncoCTF-2025/REV]$ ll theflagishere.pyc
-rwxr-xr-x 1 kali kali 4071 Feb 16 04:44 theflagishere.pyc
(kali@LEOPARD-PC)-[~/CTF-Events/BroncoCTF-2025/REV]$ file theflagishere.pyc
theflagishere.pyc: Byte-compiled Python module for CPython 3.9, timestamp-based, .py timestamp: Thu Feb 13 23:36:18 2025 UTC, .py size: 2823 bytes
(kali@LEOPARD-PC)-[~/CTF-Events/BroncoCTF-2025/REV]$ exiftool theflagishere.pyc
ExifTool Version Number         : 13.00
File Name                       : theflagishere.pyc
Directory                       : .
File Size                       : 4.1 kB
File Modification Date/Time     : 2025:02:16 04:44:42+05:00
File Access Date/Time           : 2025:02:17 22:25:32+05:00
File Inode Change Date/Time     : 2025:02:16 14:43:56+05:00
File Permissions                : -rwxr-xr-x
Error                           : Unknown file type
(kali@LEOPARD-PC)-[~/CTF-Events/BroncoCTF-2025/REV]$

  1. I had to use an online tool to convert the bytecode back into python3 code. Here is the python3 code:

# Decompiled with PyLingual (https://pylingual.io)
# Internal filename: theflagishere.py
# Bytecode version: 3.9.0beta5 (3425)
# Source timestamp: 2025-02-13 23:36:18 UTC (1739489778)

def what_do_i_do(whoKnows):
    a_st = {}
    for a in whoKnows:
        if a_st.get(a) == None:
            a_st[a] = 1
        else:
            a_st[a] += 1
    variable_name = 0
    not_a_variable_name = 'None'
    for a in a_st:
        if a_st[a] > variable_name:
            not_a_variable_name = a
            variable_name = a_st[a]
    return (not_a_variable_name, variable_name)

def char_3(): return 'm'

def i_definitely_return_the_flag():
    def notReal():
        def actually_real():
            return 'actuallyaflag'
        return actually_real
    def realFlag():
        return 'xXx___this__is_the__flag___xXx'
    return (realFlag, notReal)

def i_am_a_function_maybe(param):
    variableName = (param + 102) * 47
    for i in range(0, 100):
        variableName *= i + 1
        variableName /= i + 1
        newVariable = variableName * i
        newVariable += 100
    return chr(ord(chr(int(variableName) + 1)))

def i_do_not_know():
    realFlagHere = 'br0nc0s3c_fl4g5_4r3_345y'
    return 'long_live_long_flags'

def unrelated_statement(): return 'eggs_go_great_with_eggs'

def i_am_a_function(param):
    variableName = (param + 102) * 47
    for i in range(0, 100):
        variableName *= i + 1
        newVariable = variableName * i
        newVariable += 100
        variableName /= i + 1
    return chr(ord(chr(int(variableName))))

def i_return_a_helpful_function():
    def i_do_something(char):
        var = []
        for i in range(54, 2000):
            var.append(ord(char) / 47 - 102)
        var.reverse()
        return var.pop()
    return i_do_something

def i_return_the_flag(): return 'thisisdefinitelytheflag!'
def i(): return 'free_flag_f'
def char_0(): return i_am_a_function_maybe(i_return_a_helpful_function()(what_do_i_do(i_return_the_flag())[0]))
def char_1_4_6(): return i_am_a_function_maybe(i_return_a_helpful_function()(what_do_i_do(i_definitely_return_the_flag()[0]())[0]))
def char_2_5_9(): return i_am_a_function_maybe(i_return_a_helpful_function()(what_do_i_do(i_definitely_return_the_flag()[1]()())[0]))
def char_7(): return i_am_a_function_maybe(i_return_a_helpful_function()(what_do_i_do(interesting()()()())[0]))
def char_8(): return i_am_a_function_maybe(i_return_a_helpful_function()(what_do_i_do(i_do_not_know())[0]))
def char_10(): return i_am_a_function_maybe(i_return_a_helpful_function()(what_do_i_do(unrelated_statement())[0]))

def interesting():
    def notinteresting():
        def veryuninteresting():
            def interesting_call():
                return i
            return interesting_call
        return veryuninteresting
    return notinteresting

  1. After tinkering around a bit and trying to figure out where do all the characters go. I got the word: i_am_a_flag . Which actually was the flag:
bronco{i_am_a_flag}