How command injection works
shell_exec plus concatenation equals a shell for strangers.
Some features shell out: ping a host, convert a file, look up DNS. When the command string is built by concatenation, shell metacharacters in the input become shell syntax. ping -c 2 shop.test; id runs ping, then runs id — the semicolon ends one command and starts another, and the application never intended a second command to exist.
The separator zoo is wide: semicolons chain, pipes feed output into the next command, backticks and $() substitute command output inline. Blind cases — where output never returns — still leak through timing (sleep 10 answers yes/no) and out-of-band channels like DNS.
The fix is to never build shell strings from input: call the tool's API or library directly, or pass arguments as arrays that bypass shell parsing entirely. Where a shell is unavoidable, strict allowlists on the input value — not blocklists of characters — are the only containment that holds.
That was the whole lesson.
A free account adds the interactive part: the marking quiz, the lab if this lesson carries one, live progress, the timed exam, and a certificate employers can verify.

