Published on

TCT'24 - Misc - Secret Mission

Authors

Challenge Description

Very Easy challenge yet no solves 🫠

chal-img

Solution

Source code provided to us:

main.py
from random import randint
import time

print("Welcome, Agent! Your mission is to crack the secret code to prevent a nuclear meltdown.")

max_attempts = 340
max_time = 60

ans = randint(0, pow(10, 100))

start_time = int(time.time())
turns = 0

def setUp():
    global max_time
    print("Welcome, Agent! Your mission is to crack the secret code to prevent a nuclear meltdown.")
    max_time += 60

def main():
    global turns
    while True:
        turns += 1

        if int(time.time()) > start_time + max_time:
            print("Time's up, Agent! The meltdown cannot be stopped now!")
            break

        if turns > max_attempts:
            print("Too many failed attempts, Agent. The meltdown is inevitable now.")
            break

        inp = input("Enter your code guess: ")

        if "quit" in inp.lower():
            print("Mission aborted. You chose to retreat, Agent.")
            break

        if not inp.isdigit():
            if "restart" in inp.lower():
                setUp()
            else:
                print("Invalid input, Agent. We need a numerical code or maybe you can just restart and try again 😉")
            continue

        inp = int(inp)
        if inp > ans:
            if inp - ans > pow(10, 50):
                print("Your guess is way too high, Agent! Are you even trying?")
            else:
                print("Your guess is too high, Agent. Try a lower number.")
        elif inp < ans:
            if ans - inp > pow(10, 50):
                print("Your guess is way too low, Agent! Are you paying attention?")
            else:
                print("Your guess is too low, Agent. Try a higher number.")
        else:
            print("Congratulations, Agent! You've cracked the code and saved the world!")
            with open("flag.txt", "r") as f:
                print(f.read())
            break

if __name__ == "__main__":
    main()

The challenge expects a secret code that is a very large random number and the player has a maximum of 340 attempts and 60 seconds to guess it. With each guess, the game provides feedback on whether the guess is too high or too low. If the player guesses the correct number, it prints the flag. The challenge also includes conditions to fail if time runs out or attempts are exhausted.

We can simply use binary search to find the required random value that is stored in ans variable:

binary-search-func
def binarySearch():
    ATTEMPTS = 0
    low = 0
    high = pow(10, 100) - 1

    while low <= high:
        ATTEMPTS += 1
        mid = (low + high) // 2

        print(f"Attempt No: {ATTEMPTS}")
        print(f"Trying: {mid}")
        
        response = guessNum(mid)
        print(response)

        if "Congratulations" in response:
            break
        elif "too high" in response:
            high = mid - 1
        elif "too low" in response:
            low = mid + 1
        else:
            print("Unexpected response received...")
            break

but the problem is time. If you run this challenge locally and try to solve it, it requires 320 to 335 attempts to guess the number:

local-testing

But on remote server, our connect broke in between as it takes more than 60 seconds to make 300+ attempts:

remote-testing

that is why challenge is easily solvable locally but not on remote.

If we look closely, the solution for it is also given in the source code that says if string "restart" is given as input, it calls the setUp() function which in return increments time by 60 seconds:

def setUp():
    global max_time
    print("Welcome, Agent! Your mission is to crack the secret code to prevent a nuclear meltdown.")
    max_time += 60

So, we can simply call setUp() after every 70 to 80 attempts to ensure that our connection doesn't break.

Final script will look something like this:

solve.py
from pwn import *

io = remote(sys.argv[1], int(sys.argv[2])) if args.REMOTE else process(['python3', 'main.py'])

def guessNum(code):
    io.sendlineafter("Enter your code guess: ", str(code).encode())
    sleep(0.3)      # Ensuring response is recieved completely
    io.recvline()
    response = io.recvline().decode('utf-8')
    if "Congratulations" in response:
        extra_response = io.recvline()
        print(f"Flag: {extra_response}")
    return response


def binarySearch():
    ATTEMPTS = 0
    low = 0
    high = pow(10, 100) - 1

    while low <= high:
        ATTEMPTS += 1
        mid = (low + high) // 2
        
        if (ATTEMPTS%80) == 0:
            print(f"Attempt No: {ATTEMPTS}")
            print("Trying: restart\n")
            io.sendlineafter("Enter your code guess: ", "restart".encode())
            continue
        else:
            print(f"Attempt No: {ATTEMPTS}")
            print(f"Trying: {mid}")
            
            response = guessNum(mid)
            print(response)

            
            if "Congratulations" in response:
                break
            elif "too high" in response:
                high = mid - 1
            elif "too low" in response:
                low = mid + 1
            else:
                print("Unexpected response received...")
                break

    io.close()

if __name__ == "__main__":
    binarySearch()

Flag:

chal-img
TCT{c0ngr4t5_t1g3r_y0u_s70pp3d_7h3_4774ck}