Skip to main content

bytemancy 0 picoCTF 2026 Solution

A general skills challenge testing your understanding of ASCII encoding and how programs parse character input.

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 to understand what byte sequence the server expects.
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 ships app.py and talks about a byte sequence the server wants. Read the source and see the comparison before connecting.
    Download app.py. It prints a banner telling you exactly what to send: ASCII DECIMAL 101, three times, side-by-side, no space. ASCII 101 is the letter 'e'.
    bash
    cat app.py
    What didn't work first

    Tried: Send the literal string '101101101' to the server because the banner says 'ASCII DECIMAL 101' three times.

    The server wants three copies of the byte for 'e'. Sending 101101101 sends nine digit characters instead, and the comparison fails at once. Convert the code point 101 to its character first, then send that character three times.

    Tried: Use hexadecimal notation and send '\x65\x65\x65' as a shell argument with echo.

    In most shells, echo with single quotes prints the literal backslash-x text rather than the bytes, so the server gets nine bytes instead of three. printf interprets the hex escapes, or write the bytes from Python's buffered stdout.

    Learn more

    The ASCII (American Standard Code for Information Interchange) table maps integers 0-127 to characters. Decimal 101 is the lowercase letter 'e'. The full printable ASCII range spans 32 (space) through 126 (~). Knowing ASCII values by heart speeds up CTF challenges significantly - the most useful anchors are: 48='0', 65='A', 97='a'.

    The challenge description's phrasing "ASCII DECIMAL 101, three times" is intentionally explicit about the encoding layer: the number 101 is in decimal notation, it refers to the ASCII standard, and the character it represents is 'e' (0x65 in hex, 01100101 in binary). A beginner mistake is sending the string "101101101" (the digits) instead of "eee" (the characters).

    Netcat (nc) is a raw TCP/UDP socket tool. Piping output into nc HOST PORT sends it over the network as-is. The pipe sends the stdout of the left command to the stdin of nc, which forwards it to the server. This is the simplest way to interact with a challenge server for text-based challenges.

  2. Step 2Send the payload
    Observation
    app.py compares raw input against three copies of byte 0x65, which is ASCII 101, the letter e. Pipe exactly those three bytes over.
    The server reads a line with input() and checks it equals "\x65\x65\x65" (i.e. eee). HOST and the <PORT_FROM_INSTANCE> come from the instance launch page. echo appends \n, but input() returns the line without its terminator, so the newline never reaches the comparison and printf 'eee' works just as well. See netcat for CTF.
    bash
    echo 'eee' | nc <HOST> <PORT_FROM_INSTANCE>
    bash
    # If the server rejects the trailing newline:
    bash
    printf 'eee' | nc <HOST> <PORT_FROM_INSTANCE>
    python
    python3 -c "print('eee')" | nc <HOST> <PORT_FROM_INSTANCE>
    bash
    # Quick sanity check that 101 maps to 'e':
    python
    python3 -c 'print(chr(101))'

    Expected output

    e
    What didn't work first

    Tried: Assume echo's trailing newline breaks the comparison and go hunting for a way to suppress it.

    app.py reads with input(), which hands back the line minus its terminator, so the newline is gone before the comparison runs. echo 'eee' and printf 'eee' both pass. A trailing space is the thing that actually breaks it, because input() does not strip those.

    Tried: Send the payload before reading app.py and guess the input is '101' (the decimal number as a string).

    Without reading the source it is easy to assume the server wants the decimal digits. The string 101 is three different bytes, while the server expects three copies of 0x65. The source says so explicitly.

    Learn more

    The hex representation \x65\x65\x65 and the character string 'eee' are identical at the byte level. Decompose: 101 = 0x65 = 0b01100101 = 'e'. In Python, b'\x65' == b'e' evaluates to True. Hex escape sequences in byte strings are just an alternative notation for the same bytes, useful when the byte value does not correspond to a printable character.

    echo adds a trailing newline (\n, byte 10) to its output by default. Most challenge servers read input line-by-line and strip the newline before comparing, so this is usually fine. If a server requires no trailing newline, use echo -n or Python's sys.stdout.buffer.write(b'eee') without the newline.

    These bytemancy challenges form a series that progressively increases difficulty: from simple text to large repetitions to raw non-printable bytes to address-lookup challenges. They teach the distinction between character encodings, byte values, and how terminals and shells handle binary data - skills that are foundational for binary exploitation and network protocol work.

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{pr1n74813_ch4r5_...}

app.py asks for ASCII decimal 101 × 3, no spaces. ASCII 101 = 'e', so send the three-character string 'eee'.

Key takeaway

Every character a computer stores is a number, and ASCII fixes the mapping between 0 to 127 and printable characters. Mistaking a code point for the digits that spell it is a classic error, and it appears wherever data crosses an encoding boundary: protocol parsers, web forms, embedded firmware. Recognizing those layers, and reaching for chr() and ord() to move between them, matters as much in code review as in exploitation.

Related reading

Useful tools for General Skills

Where to go next