The cheapest possible check. Look before you build a callback.
If the command's output comes back in the response you get an instant, readable, report-ready answer with no infrastructure at all. Try this first — it costs one request.
What a hit looks like — uid=33(www-data) in the body, a Windows username, or the page rendering with an extra line that wasn't there. Also watch for a shorter response: |id replaces the original output rather than adding to it.
02Nothing came back — confirm out-of-band
Now build a channel. Out-of-band beats time-based when the box can reach the network.
Baseline first. Time the endpoint with a zero-second sleep before you believe a five-second one, and send the pair — sleep 0 then sleep 5 — so the difference is your evidence rather than the absolute number.
03Separators
Which one works tells you how your input is being used.
Windows cmd and PowerShell take different separators. If ; does nothing but & works, you're in cmd — which also tells you sleep won't exist and you want timeout instead.
05Argument injection
No shell involved, but you control an argument. Often missed entirely.
ARGUMENT INJECTION7
curl
-o /var/www/html/shell.phpWrites the response body wherever the process can write.
This is where an application that carefully avoids system() still hands you execution, because it passes your filename straight into an argument array. Look for it anywhere a file name, URL or hostname is forwarded to a binary.