Bypass Me picoCTF 2026 Solution

Published: March 20, 2026

Description

Your task is to analyze and exploit a password-protected binary called bypassme.bin. Instead of guessing the password, reverse engineer or debug the program to bypass the authentication logic and retrieve the hidden flag.

Launch the challenge instance and connect.

The binary bypassme.bin will be provided on the remote server.

  1. Step 1Analyse the binary statically
    Run file first (ltrace only works on dynamically linked binaries), then strings, objdump, and radare2's auto-analysis. See Ghidra if you prefer a GUI decompiler.
    bash
    file bypassme.bin   # confirm dynamically linked before using ltrace
    bash
    strings bypassme.bin | grep -iE 'pass|secret|correct|wrong'
    bash
    objdump -d bypassme.bin | grep -A 20 'strcmp\|cmp'
    bash
    r2 -A bypassme.bin
    Learn more

    Static analysis examines a binary without running it. strings is the fastest static check - any hardcoded password stored as a null-terminated C string will appear in the output. After that, objdump -d disassembles the binary and lets you read the assembly. Searching for strcmp or cmp instructions near string references usually locates the authentication logic quickly.

    Radare2 (r2) is a full reverse engineering framework. After opening a binary with r2 -A (which auto-analyzes), you can use afl to list functions, pdf @ main to print the disassembly of main, and iz to list all strings. It also has a visual mode (V) and a graphing mode (VV) for following control flow.

    The difference between strcmp and a custom comparison loop matters for exploitation: strcmp is interceptable by ltrace and its arguments are visible in a debugger, while a custom byte-by-byte loop requires reading the comparison address to find where the expected password lives in memory. Both are ultimately readable with the right tool.

  2. Step 2Pick a path: leak, patch, or flip eflags
    Three approaches. (A) ltrace the dynamic strcmp call and read the second argument off the line. (B) Patch the conditional jump in radare2. (C) Force the success branch in GDB by toggling the zero flag. Concrete commands below.
    bash
    # (A) Leak via ltrace - sample line: strcmp("input", "correct_password") = -1
    bash
    ltrace -e strcmp ./bypassme.bin <<< 'guess'
    bash
    bash
    # (B) Find the jump in r2 then patch it
    bash
    r2 -A bypassme.bin
    bash
    # In r2:
    bash
    #   pdf @ main          # print decompiled main
    bash
    #   /c jne              # find conditional jumps
    bash
    #   s 0xADDR_OF_JNE     # seek to it
    bash
    #   q
    bash
    r2 -w bypassme.bin
    bash
    # In r2: s 0xADDR_OF_JNE ; wa jmp 0xSUCCESS_ADDR
    bash
    bash
    # (C) GDB runtime flip - no on-disk patch needed
    bash
    gdb ./bypassme.bin
    bash
    # In GDB: b *0xADDR_OF_JNE ; r ; set $eflags |= 0x40 ; c   # 0x40 sets ZF
    Learn more

    What ltrace shows. A typical leak line looks like strcmp("hunter2", "correct_password") = -1: ltrace prints both args because it hooked the PLT entry for strcmp. The non-zero return tells you the strings differ; copy the second argument and feed it to the binary as input.

    Binary patching. In x86, jne is 75 XX. Replacing 75 with 74 flips it to je; with EB it becomes jmp (always taken). Radare2's write mode (-w) plus wa (write assembly) lets you write human-readable instructions and r2 encodes them. pwntools' ELF.write() is the scriptable alternative.

    GDB eflags trick. The zero flag (ZF) lives at bit 6 of the EFLAGS register. After the cmp that compares your input to the expected password, ZF is set if they were equal. Set ZF manually with set $eflags |= 0x40 before the jne executes and the jump is not taken, falling through to the success branch. This is the cleanest path because it leaves no trace on disk.

    In real engagements, patching is rare (you need write access). It's standard in cracking and malware analysis. Defenses include code signing and runtime self-hash checks.

  3. Step 3Run the patched binary or use the password
    Either run the patched binary which skips the password check, or supply the extracted password to the original.
    bash
    echo 'EXTRACTED_PASSWORD' | ./bypassme.bin
    bash
    ./bypassme_patched.bin
    Learn more

    Both approaches arrive at the same result through different paths. Password extraction keeps the binary unmodified and uses knowledge of the secret to authenticate legitimately. Binary patching modifies program logic to skip authentication entirely, which is more powerful because it works even when passwords are derived from complex computations.

    In a real penetration test, binary patching is rarely used (you need write access to the binary), but it is a standard technique in software cracking (removing license checks) and malware analysis (disabling anti-analysis tricks). Modern defenses include code signing (the OS refuses to run tampered binaries) and integrity checks (the program hashes its own code at startup).

Flag

picoCTF{byp4ss_m3_...}

The authentication bypass works either by extracting the hardcoded password or patching the comparison branch.

How to prevent this

Hardcoded secrets in client binaries are visible to anyone with strings(1). Defense lives on the server.

  • Never put a meaningful secret in a binary you ship. Strings, ltrace, IDA, and Ghidra all surface it within minutes. Treat client-side code as untrusted.
  • Authenticate against a server: the client sends credentials, the server checks them, the server returns the protected data. The success branch should not exist on the client at all.
  • For local-only license / DRM use cases, combine code signing, runtime self-hash checks, and obfuscation as defense-in-depth. None of these are unbreakable, but together they raise the cost above the value of cracking.

Want more picoCTF 2026 writeups?

Tools used in this challenge

Related reading

What to try next