bytemancy 2 picoCTF 2026 Solution

Published: March 20, 2026

Description

Can you conjure the right bytes? Download app.py and recover the exact input the server expects.

Download and read app.py.

Launch the challenge instance and connect via netcat.

bash
cat app.py

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1
    Read the source code
    Observation
    I noticed the challenge provided app.py and asked me to 'conjure the right bytes,' which suggested the server was doing an exact byte comparison, so reading the source was the first step to discover what byte values and I/O mode were being used.
    Download app.py. It reads via sys.stdin.buffer (raw bytes) and checks that the input equals b'\xff\xff\xff' (hex byte 0xFF, three times, no space). The key difference from bytemancy-0/1 is that it reads raw binary, not text, so you must send literal byte 0xFF, not the string 'ff'.
    bash
    cat app.py
    What didn't work first

    Tried: Sending the string 'ffffff' or 'ff ff ff' to the server via netcat directly.

    The server compares the input to b'\xff\xff\xff', which is three raw bytes each with decimal value 255. Typing 'ff' sends the ASCII characters 'f' and 'f' (bytes 102 and 102), not the single byte 0xFF. The comparison fails silently and no flag is returned.

    Tried: Using sys.stdin instead of sys.stdin.buffer and trying to decode 0xFF as a Unicode character.

    0xFF is not a valid standalone UTF-8 byte, so Python's text-mode sys.stdin raises a UnicodeDecodeError when it tries to decode it. The server uses sys.stdin.buffer specifically to bypass the encoding layer and receive raw bytes; text mode cannot handle byte values above 127 that don't form valid UTF-8 sequences.

    Learn more

    The critical distinction here is between text mode and binary mode I/O. Python's sys.stdin is a text stream that decodes bytes to Unicode strings using the terminal encoding (usually UTF-8). sys.stdin.buffer is the underlying binary stream - it gives you raw bytes without any Unicode decoding. A server using sys.stdin.buffer.read() compares raw bytes, so you must send actual byte values, not their text representations.

    Byte 0xFF (255 decimal) is the highest possible byte value. It is not a valid UTF-8 byte in any position (UTF-8 never uses 0xFF -- valid multi-byte sequences use leading bytes 0xC0-0xF7 and continuation bytes 0x80-0xBF, so 0xFF and 0xFE are explicitly excluded from all UTF-8 encodings) and is not printable ASCII. Terminals typically cannot type or display it directly, which is why you need either printf with octal/hex escapes or a Python socket that sends b'\xff' as a raw byte.

    printf '\xff\xff\xff\n' in bash interprets \xff as a hex escape and outputs the literal byte 0xFF. This is different from echo '\xff', which on most shells outputs the six characters backslash, x, f, f (text) rather than the binary byte. Understanding this shell behavior is essential for binary exploitation work where you need to inject exact byte sequences.

  2. Step 2
    Send the raw bytes
    Observation
    I noticed app.py uses sys.stdin.buffer and compares input to b'\xff\xff\xff', which meant I needed to send literal 0xFF bytes over the wire and not text representations, pointing to pwntools or printf with hex escapes as the only reliable methods.
    Send three raw 0xFF bytes plus a newline. The challenge hint explicitly recommends pwntools for this. Note that naively piping python3 -c "print(b'\xff'*3)" corrupts the bytes: Python's text-mode print re-encodes 0xFF as a multi-byte UTF-8 sequence before it ever reaches the server. You must use a method that stays in binary mode throughout, such as pwntools or sys.stdout.buffer.write. See Python for CTF.
    bash
    # Primary: pwntools (stays in binary mode, no encoding corruption):
    python
    python3 - <<'PY'
    from pwn import *
    p = remote('<HOST>', <PORT_FROM_INSTANCE>)
    p.sendline(b'\xff\xff\xff')
    p.interactive()
    PY
    bash
    # Alternative (bash only - dash/busybox printf may not honor \x escapes):
    bash
    printf '\xff\xff\xff\n' | nc <HOST> <PORT_FROM_INSTANCE>
    What didn't work first

    Tried: Using python3 -c "print(b'\xff'*3)" piped into nc to send the bytes.

    Python's print() uses text-mode stdout by default, which re-encodes the byte value 0xFF into a multi-byte UTF-8 escape sequence before it leaves the process. The server receives corrupted bytes instead of three raw 0xFF values. You must use sys.stdout.buffer.write(b'\xff\xff\xff\n') or pwntools to stay in binary mode throughout.

    Tried: Using echo '\xff\xff\xff' piped into nc instead of printf.

    On most shells, echo does not interpret backslash-hex escapes by default - it outputs the literal six-character string '\xff' three times. printf with the \x or octal \377 escape is the POSIX-specified way to emit raw byte values from the shell. Some shells (bash with echo -e) can interpret escapes, but printf is more portable and explicit.

    Learn more

    Python byte literals (b'\xff') let you specify exact byte values using hex escapes. The bytes object is a sequence of integers 0-255 - completely independent of any character encoding. When you call socket.sendall(b'\xff\xff\xff\n'), Python sends four bytes to the server: 255, 255, 255, 10 (the newline is byte 10 in ASCII).

    This challenge teaches the concept of raw binary protocol interaction, which is essential for network binary exploitation. Tools like pwntools are designed specifically for this: p.sendline(b'\xff\xff\xff') sends the bytes plus a newline, and p.recv() reads raw bytes back. You never need to worry about encoding layers because pwntools stays in binary mode throughout.

    The progression from bytemancy-0 (printable ASCII) to bytemancy-2 (raw non-printable bytes) mirrors real exploit development: shellcode and ROP gadget addresses contain arbitrary byte values, many of which are non-printable. Mastering raw byte I/O is a prerequisite for buffer overflow and format string exploitation.

Interactive tools
  • Base64 & Base32 DecoderDecode Base64 and Base32 strings with auto-detection. Multi-layer mode unwraps nested encodings automatically.
  • Recipe ChainStack decoders into a pipeline: Base64, hex, ROT, XOR, Morse, URL, Atbash, Vigenère, and more. Magic mode auto-discovers the chain. Bookmark the URL to save it.
  • Number Base ConverterConvert numbers between binary, octal, decimal, and hexadecimal instantly. Enter any value and see all four bases update in real time.

Flag

Reveal flag

picoCTF{byt3m4ncy_2_...}

app.py reads raw bytes via sys.stdin.buffer and expects the three literal bytes 0xFF 0xFF 0xFF. Use printf '\xff\xff\xff\n' | nc; sending the text string 'ff' will not work.

Key takeaway

Programs distinguish between text mode I/O, which applies a character encoding like UTF-8, and binary mode I/O, which passes raw byte values unchanged. Non-printable bytes such as 0xFF cannot be entered at a keyboard or sent through text-mode streams without corruption, so exploit tools like pwntools and shell utilities like printf with hex escapes exist specifically to transmit exact byte sequences. This distinction is critical in binary exploitation, where shellcode, addresses, and padding all contain arbitrary byte values that would be mangled by any encoding layer.

Related reading

Want more picoCTF 2026 writeups?

Useful tools for General Skills

What to try next