Skip to main content

file-run2 picoCTF 2022 Solution

Run a provided Linux binary and figure out the right input it needs to print the flag.

Published: July 20, 2023Updated: August 25, 2026

Description

Another simple binary: it expects one argument (Hello!). Provide it on the command line to print the flag.

Make the binary executable (chmod +x run).

Execute it with the required argument: ./run Hello!.

bash
chmod +x run
bash
./run 'Hello!'
bash
ltrace ./run wrong-guess
bash
./run 'Hello!' | grep -oE 'picoCTF\{[^}]+\}'

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 prompt carefully
    Observation
    The description states the required argument outright. The only obstacle left is passing it to the binary without the shell interpreting the exclamation mark.
    The description literally tells you to pass Hello!. Without the argument the program just prompts you again.
    What didn't work first

    Tried: Running ./run Hello! in bash and getting an unexpected error or wrong-argument message.

    Bash treats ! as a history expansion character in interactive shells, so Hello! can silently expand to something else before the program ever sees it. Single-quoting the argument as ./run 'Hello!' prevents the shell from interpreting the exclamation mark.

    Tried: Running ./run with no arguments and waiting for the program to prompt for input.

    Command-line arguments are not the same as standard input. The program reads from argv[1] at startup, not from stdin, so typing at the prompt does nothing. The argument must be passed on the same line as the binary name.

    Learn more

    Command-line arguments are values passed to a program at launch, available inside the program via the argc / argv array in C, or sys.argv in Python. They are separate from environment variables and standard input - three distinct ways a program receives data from its caller.

    A binary that compares argv[1] against a fixed string is a basic form of input gating. Identifying what argument it wants is the same problem as the bbbbloat magic-constant challenge, just with a string instead of an integer.

    Worked ltrace example. Even with a wrong guess, ltrace leaks the expected string:

    $ ltrace ./run hello
    __libc_start_main(0x5588f9e0d1a9, 2, 0x7ffd..., <unfinished ...>
    strcmp("hello", "Hello!")                    = 32
    puts("Won't you say 'Hello!' to me first?")  = 36
    +++ exited (status 0) +++

    The second argument to strcmp is the expected value. That single line solves the challenge.

    The exclamation mark only matters in interactive shells. Bash (and zsh in some configurations) treats ! as history expansion when reading commands typed at the prompt - so ./run Hello! can become ./run HelloFOO if your history matches. The fix is single-quoting: ./run 'Hello!'. Inside scripts, in pipelines, in non-interactive shells (sh, dash), or when the command is not at a TTY prompt, history expansion is disabled and the unquoted form is fine.

    When the expected argument is unknown. Walk a recon ladder before reaching for the disassembler:

    1. strings run | head -40 - format strings, error messages, and the comparison constant itself often appear verbatim.
    2. ltrace ./run with no arg, or with a guess - hooks strcmp/strncmp/memcmp and prints both operands.
    3. ltrace ./run <guess> - if you suspect the argument value but want confirmation.
    4. strace -e trace=write ./run <guess> - shows what the program writes to stdout, including hint messages like "Won't you say 'Hello!' to me first?".
    5. Last resort: open in Ghidra, find main, read the comparison.

    Linux-side recon shortcuts are collected in Linux CLI for CTF.

  2. Step 2Capture the flag
    Observation
    The binary prints the flag inside a sentence. Pipe the output through a grep pattern matching picoCTF{...} to pull out just the token, rather than copying it by hand.
    Pipe the output through grep -oE 'picoCTF\{[^}]+\}' to isolate the flag token regardless of the surrounding wording.
    What didn't work first

    Tried: Manually copying the flag text from the terminal output by hand.

    This works for a single challenge but is error-prone with long flag strings that contain similar-looking characters. Piping to grep -oE 'picoCTF\{[^}]+\}' extracts just the flag token reliably and is faster when automating multiple challenges.

    Tried: Using cut -d ' ' -f4 and getting an empty or wrong field because the output sentence has a different word count.

    The field number cut needs depends on the exact wording of the output line, so any change to that wording shifts the index. Matching the flag by pattern instead of position survives whatever text surrounds it.

    Learn more

    Output parsing with shell tools like grep, cut, and awk is a fundamental skill for working efficiently in the terminal. Rather than reading the entire output and copying the flag by hand, piping to grep -oE 'picoCTF{[^}]+}' extracts precisely the token you need whatever sentence surrounds it, which is what makes it safe to reuse across challenges.

    Position-based extraction works too when you already know the wording: this binary prints The flag is: picoCTF{...}, so cut -d ' ' -f4 or awk '{print $4}' both land on the flag. awk handles variable amounts of whitespace between fields, unlike cut, which treats each delimiter character separately. Both break the moment the sentence changes, which is why the pattern match is the safer default.

    Shell pipelines chain commands so the output of one becomes the input of the next. This composability is one of the defining strengths of Unix-style tooling. Instead of writing a custom script to parse program output, you string together small, single-purpose utilities. The same pipeline pattern appears constantly in security work: capturing output from a scanner, filtering it for relevant entries, extracting fields, and writing results to a file - all in a single one-liner.

    tr, sort, uniq, and wc are other commonly paired tools. For example, strings binary | sort | uniq -c | sort -rn produces a frequency-ranked list of all printable strings in a binary, which can highlight repeated constants or likely passwords. Building fluency with pipeline composition will save significant time during time-pressured CTF events.

Interactive tools
  • File Magic IdentifierIdentify file types from magic numbers. Paste hex bytes or drop a file to detect PNG, JPEG, ZIP, PDF, ELF, PCAP, SQLite, and dozens of other formats.
  • Strings ExtractorPull printable text from any binary, library, or image. ASCII and UTF-16 detection, configurable minimum length, flag-like highlight, no command line needed.
  • Regex TesterTest regular expressions against a string with live match highlighting, flag toggles, and common CTF pattern shortcuts.

Flag

Reveal flag

picoCTF{F1r57_4rgum3n7_be0714da}

Introductory warm-up for command-line arguments.

Key takeaway

A binary that gates behavior behind a specific command-line argument is doing basic input validation, and the expected value is almost always recoverable without disassembly. ltrace intercepts strcmp and its variants and prints both operands at runtime, so even a wrong guess reveals the string being compared against. That generalizes to any binary checking input against a hardcoded secret: password checks, license key validation, firmware authentication.

Related reading

Useful tools for Reverse Engineering

Where to go next