Skip to main content

bytemancy 2 picoCTF 2026 Solution

The server compares your input against raw byte values, so send those bytes rather than their printable spellings.

Published: March 20, 2026Updated: September 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 1Read the source code
    Observation
    The challenge hands over app.py and asks for the right bytes, so an exact byte comparison is happening. Read the source for the values and the I/O mode it uses.
    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 wants three raw bytes of value 255. Typing ff sends two ASCII letters instead, and the comparison fails silently with no flag.

    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 text-mode stdin raises a decode error on it. The server uses the buffer interface precisely to skip the encoding layer, because text mode cannot carry byte values above 127 that do not form valid 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 0xC2-0xF4 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 four 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 2Send the raw bytes
    Observation
    app.py reads from sys.stdin.buffer and compares against three 0xFF bytes, so literal bytes have to go over the wire, not a text representation. pwntools or printf with hex escapes are the reliable ways to send them.
    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)" sends the wrong thing: print of a bytes object writes its repr, so the server receives the 15 ASCII characters b'\xff\xff\xff'. Dropping the b prefix is no better, because text-mode stdout then re-encodes each U+00FF as the two UTF-8 bytes c3 bf. 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.

    print() on a bytes object writes its repr, so what goes out is the literal text b'\xff\xff\xff'. Print the str form instead and text-mode stdout re-encodes each character as the two UTF-8 bytes c3 bf. Either way the server sees the wrong bytes. Write through sys.stdout.buffer, or use pwntools, and stay in binary mode the whole way.

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

    Most shells do not interpret backslash-hex in echo, so you get the literal text three times over. printf with a hex or octal escape is the POSIX way to emit raw bytes. Some shells interpret escapes with echo -e, but printf is 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{3ff5_4_d4yz_...}

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 text-mode I/O, which applies an encoding like UTF-8, from binary mode, which passes bytes through unchanged. A non-printable byte cannot be typed at a keyboard or pushed through a text-mode stream without corruption, which is why pwntools and printf with hex escapes exist. The distinction is central to binary exploitation, where shellcode, addresses, and padding all contain arbitrary byte values an encoding layer would mangle.

Related reading

Useful tools for General Skills

Where to go next