Dev.to Security 🔐 Cybersecurity 👁 0 📖 3 min read

PicoCTF Crackme-py Writeup — Reverse a Python Password Check

The Python script compares user input, after applying a transformation to it (base64 encoding, character shift, etc.), to a hardcoded string. Instead of guessing via brute force, we read the code to identify the transfor

The Python script compares user input, after applying a transformation to it (base64 encoding, character shift, etc.), to a hardcoded string. Instead of guessing via brute force, we read the code to identify the transformation, then reverse it mathematically to directly recover the correct password.

  • Platform: picoGym
  • Category: Rev Eng
  • Points: 250 pts
  • Difficulty: Intermediate
  • Tools: python3source reading

Challenge description

The challenge provides a script, crackme.py, which asks for a password and prints a success or failure message based on the result of a comparison:

$ python3 crackme.py
Enter the password: test123
Wrong! Try again.

Unlike a binary crackme that would require a disassembler, here the source code is directly readable — the goal isn't to deobfuscate assembly, but to understand the transformation logic and turn it against itself.

Step 1 — Read the code and locate the comparison

We open crackme.py and look directly for the line that decides success or failure — usually an if comparing a function's result to a constant:

$ cat crackme.py
import base64

EXPECTED = "cGljb0NURntmNGs" # truncated, for example

def transform(pw):
    encoded = base64.b64encode(pw.encode())
    return encoded.decode()

user_input = input("Enter the password: ")

if transform(user_input) == EXPECTED:
    print("Correct! Here's your flag.")
else:
    print("Wrong! Try again.")

The logic is clear: the user's input goes through transform(), and the result is compared to the constant EXPECTED, hardcoded in the file.

Step 2 — Identify the transformation

We look at the body of transform(): here it's a simple base64 encoding applied to the input. Other variants of this challenge may use a Caesar-style character shift, an XOR with a fixed key, or a string reversal — the principle stays the same: precisely identify the operation being performed.

def transform(pw):
    encoded = base64.b64encode(pw.encode())
    return encoded.decode()

# possible variant seen on other versions of the challenge:
# def transform(pw):
#     return ''.join(chr(ord(c) + 3) for c in pw)

The key point: base64.b64encode is a perfectly reversible operation — there's an exact inverse function, base64.b64decode, that loses no information. It's not a one-way hash like MD5 or SHA-256, so there's no need for brute force whatsoever.

Step 3 — Reverse the transformation

Since EXPECTED is known (hardcoded in the file) and the transformation is reversible, we just need to apply the inverse operation directly to EXPECTED to recover the expected password, without ever having to guess or test anything:

import base64

EXPECTED = "cGljb0NURntmNGs..." # full value copied from crackme.py

password = base64.b64decode(EXPECTED).decode()
print(password)
# -> picoCTF{***} (recovered password, masked here)

# If the transformation had been a +3 shift on each character,
# the inverse would simply be a -3 shift:
# password = ''.join(chr(ord(c) - 3) for c in EXPECTED)

In a handful of lines, the expected password falls out directly — no attempts, no iteration, just the exact mathematical inverse of the operation applied by the script.

Step 4 — Verify and grab the flag

We rerun the original script and enter the recovered password:

$ python3 crackme.py
Enter the password: picoCTF{***}
Correct! Here's your flag.
picoCTF{***}

The success message appears, confirming the transformation was correctly reversed, and the flag is revealed.

🚩 picoCTF{ flag intentionally hidden }

The flag is deliberately hidden — follow the method, you've earned it. 💪

Key takeaways

This challenge illustrates a fundamental distinction in reverse engineering and applied cryptography:

  • As soon as a transformation is reversible (encoding, simple arithmetic shift), mathematically inverting the check is always faster and more reliable than brute force
  • The real lesson of this challenge is to spot when an operation is invertible before choosing an attack strategy — encoding ≠ one-way hash
  • Carefully reading the source code before acting often saves hours of unnecessary brute forcing

Originally published on CTFdojo — join the CTFdojo Discord to discuss writeups and get notified about new ones.

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.