not crypto

Published: April 2, 2026

Description

There's crypto in here, but the challenge isn't about the crypto. Find another way.

Download the not-crypto binary from the challenge page.

Make it executable: chmod +x not-crypto

chmod +x not-crypto

Solution

  1. Step 1Analyze in Ghidra
    Load the binary in Ghidra and locate main. It applies a complex AES-like transformation to your 64-byte input and compares the result to a hardcoded encrypted value using memcmp. Reversing the AES math is the hard path -- instead, look for a smarter approach.
    Learn more

    Ghidra is a free, open-source reverse engineering suite developed by the NSA and released publicly in 2019. Its decompiler converts x86/x86-64 (and many other architectures) machine code into C-like pseudocode, making it far easier to understand a binary's logic than reading raw assembly. For this challenge, Ghidra reveals the comparison structure and identifies the memcmp call.

    The challenge name "not-crypto" is a deliberate hint: the binary contains cryptographic operations, but solving it does not require understanding or reversing the cryptography. This is a common CTF design philosophy -- the intended solution exploits a structural weakness (the plaintext flag being in memory at the comparison point) rather than breaking the cryptographic algorithm itself.

    Reversing AES, even a simplified variant, is mathematically intensive and time-consuming under CTF time constraints. Recognizing when a challenge is designed to be solved via dynamic analysis rather than static cryptanalysis is an important meta-skill in CTF competitions.

  2. Step 2Attach GDB and find the memcmp call
    PIE is enabled, so get the base address first. Then set a breakpoint at the memcmp call inside the comparison function. Ghidra shows the call site offset.
    gdb ./not-crypto
    break main
    run $(python3 -c "print('A'*64)")
    info proc map
    Learn more

    PIE (Position Independent Executable) is a compiler/linker option that makes the binary load at a randomly chosen base address (courtesy of ASLR) rather than a fixed address. This means the actual address of any instruction = base address + offset shown in Ghidra/objdump. Without knowing the base address, breakpoints set at absolute addresses will fail.

    info proc map in GDB displays the current process's memory map, including the address range where the main executable was loaded. The start of the .text segment (the first executable mapping) is the base address. Adding the Ghidra-reported offset of the memcmp call to this base gives the runtime address to break on.

    Running with 64 'A' characters ensures the program proceeds far enough to reach the comparison (it expects a 64-byte input). The padding value doesn't matter -- the goal is to reach the memcmp breakpoint where the expected flag value is loaded into a register, regardless of what the user input is.

  3. Step 3Break at memcmp and read the expected flag
    Set a breakpoint at the memcmp call site (PIE base + offset shown in Ghidra, e.g. 0x13b9). Run with 64 junk bytes. When the breakpoint hits, the rdi register points to the expected plaintext flag.
    break *0x<base>+0x13b9
    continue
    x/s $rdi
    Learn more

    memcmp(ptr1, ptr2, n) compares n bytes at two memory addresses and returns 0 if they are identical. On x86-64, function arguments are passed in registers: the first argument goes in rdi, the second in rsi, and the count in rdx. At the breakpoint, rdi and rsi point to the two buffers being compared -- one is your transformed input, the other is the expected (decrypted) flag.

    x/s $rdi is GDB's examine command: x for examine, /s to interpret the memory as a null-terminated string. It reads and displays the bytes at the address in rdi as text. If the flag is stored as a null-terminated string in that buffer, this single command reveals the entire expected value without any cryptographic analysis.

    This technique -- breaking at comparison functions to read expected values -- is applicable to many CTF challenges and real-world scenarios: license key validation, password checking, and authentication tokens that are compared in memory are all vulnerable to this approach. Countermeasures include constant-time comparison (not vulnerable to timing attacks but still readable in a debugger), and anti-debugging checks that detect GDB's presence via ptrace(PTRACE_TRACEME).

Flag

picoCTF{...}

Debugger-based approaches often bypass complex encryption -- if memcmp compares your input against the decrypted flag, reading the $rdi register at the breakpoint reveals the expected value before any comparison occurs.

More Reverse Engineering