Description
Another simple binary: it expects one argument (Hello!). Provide it on the command line to print the flag.
Setup
Make the binary executable (chmod +x run).
Execute it with the required argument: ./run Hello!.
chmod +x run./run 'Hello!'ltrace ./run wrong-guess./run 'Hello!' | grep -oE 'picoCTF\{[^}]+\}'Solution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1
Read the prompt carefullyObservationI noticed the challenge description explicitly stated the required argument (Hello!), which suggested the only obstacle was passing it correctly to the binary without the shell misinterpreting the exclamation mark.The description literally tells you to passHello!. 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, soHello!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
./runwith 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/argvarray in C, orsys.argvin 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(0x401136, 2, 0x7ffc..., 0x401200 <unfinished ...> strcmp("hello", "Hello!") = -1 puts("Wrong argument!") = 16 +++ exited (status 1) +++The second argument to
strcmpis 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 HelloFOOif 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:
strings run | head -40- format strings, error messages, and the comparison constant itself often appear verbatim.ltrace ./runwith no arg, or with a guess - hooksstrcmp/strncmp/memcmpand prints both operands.ltrace ./run <guess>- if you suspect the argument value but want confirmation.strace -e trace=write ./run <guess>- shows what the program writes to stdout, including hint messages like "Wrong argument".- Last resort: open in Ghidra, find
main, read the comparison.
Linux-side recon shortcuts are collected in Linux CLI for CTF.
Step 2
Capture the flagObservationI noticed that the binary prints the flag embedded in a sentence, which suggested piping the output throughgrep -oE 'picoCTF\{[^}]+\}'to extract just the flag token reliably rather than copying it by hand.Usecutto isolate the fourth space-delimited field for a clean flag string.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 ' ' -f4and getting an empty or wrong field because the output sentence has a different word count.The field number passed to
cutdepends on the exact wording of the program's output line. If the output changes, the field index shifts. Usinggrep -oE 'picoCTF\{[^}]+\}'instead extracts the flag by pattern rather than position, making it robust to any surrounding text.Learn more
Output parsing with shell tools like
cut,awk, andgrepis a fundamental skill for working efficiently in the terminal. Rather than reading the entire output and copying the flag by hand, piping tocut -d ' ' -f4extracts precisely the token you need, which is useful when automating or when the flag is embedded in a longer sentence.As an alternative,
awk '{print $4}'achieves the same result and handles variable amounts of whitespace between fields (unlikecut, which treats each delimiter character separately). Knowing both gives you flexibility depending on the structure of the output you're parsing.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 -rnproduces 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.
Flag
Reveal flag
picoCTF{F1r57_4rgum3n7_be07...}
Introductory warm-up for command-line arguments.