Command-line detection: equivalent effects, syntax variants and telemetry
Two command lines can produce the same output but leave different text in telemetry. A small lab method for testing CLI rules without claiming an EDR bypass.
On 20 October, we asked base64 to decode T0sK in two ways. Both --decode and --dec produced the same three bytes: 4f 4b 0a, or OK followed by a newline. A literal search for --decode would only match one of the command lines. This small gap captures a more useful problem than theatrical obfuscation: a program interprets arguments with its own parser, while a detection rule may only inspect a string. The point of an offensive test is to measure that gap, then check what the sensor actually recorded.
The starting point is Wietze Beukema’s SecSea 2k24 presentation, You can run AND you can hide: Obfuscating Command Lines. Its 37 slides explore synonymous syntax on Windows, Linux and macOS. The idea is his; we ran the harmless Linux experiments below separately on our own host. We did not replay his Windows demonstrations, test an EDR rule, or establish a product bypass.
The program and the rule read different things
A command line passes through several interpreters. The shell first splits and expands what was typed; the program then gives meaning to its arguments. A sensor finally records some representation of the execution, depending on the operating system, configuration and collection point. The same result, the same arguments and the same telemetry are three different claims. Beukema explores that distinction in his research on synonymous command lines.
The first test is small enough to check by eye. od prints the bytes, so two outputs that merely look identical do not become proof of equivalence:
printf 'T0sK' | base64 --decode | od -An -tx1
printf 'T0sK' | base64 --dec | od -An -tx1
# Both returned on our host: 4f 4b 0a
With GNU coreutils base64 9.4 on our host, --dec unambiguously abbreviates --decode. This depends on the parser and its option set: another option with the same prefix could make an abbreviation ambiguous. It is not a universal property of Linux, and a rule that already handles the short form has nothing to bypass. The GNU Coreutils manual describes unambiguous long-option abbreviations; the slides apply the idea to base64.
The second test made us inspect the actual program rather than infer semantics from the spelling. We supplied identical hexadecimal bytes to xxd in three ways:
printf '4f4b0a' | xxd -p -r | od -An -tx1
printf '4f4b0a' | xxd -plain -revert | od -An -tx1
printf '4f4b0a' | xxd -password -reset | od -An -tx1
# All three local outputs: 4f 4b 0a
Our executable identifies itself as xxd 2023-10-25. -password and -reset look misleading, but this parser accepts the strings and produces the same result here as -p -r. Beukema’s slides point to this family of variants; they cannot replace a replay on the version actually present in an environment. These three outputs do not prove that every input, platform or xxd build behaves alike.
Rejected variants belong in the test record
In a lab, we would start from a harmless reference command, then change one argument at a time. For each candidate, record the binary version, input, exit status, output, errors and any file or network effect. Include a deliberately invalid case: it checks that the harness can recognise failure instead of marking every experiment “equivalent”. Only then compare the runs with collected events and a specific rule’s decision. The useful record has four columns: submitted command, executed meaning, recorded event and rule outcome. A terminal screenshot fills only the second one.
If the sensor does not collect arguments, the test cannot evaluate the signature. Alerts triggered by the process tree or disk effects must remain in the results, even if the textual rule missed the command. A rule that recognises only --decode has a gap limited to the version and telemetry tested.
The Windows examples in the presentation, including findstr option separators, illustrate the same issue at another layer: each utility decides what it accepts. Microsoft’s documented findstr syntax does not enumerate every variant shown in the slides. Without a replay on current Windows, we treat those as the author’s 2024 observations rather than guaranteed behaviour today. The presentation also covers memory-dumping tools; reproducing those payloads would add nothing to this benign method.
Include process behaviour in detection rules
A textual rule tied to one option spelling should be tested against variants accepted by the monitored binary. To supplement this detection, correlate the command with the program’s identity, parent process, file activity and network connections. Beukema also recommends this correlation in his presentation.
The test record should include both invocations, their observed effects, collected events and the alerts produced by each rule. A claim of evasion requires checking every active detection in that environment.
#rootzero
A question about your exposure?
Write to us. Someone from the technical team will answer, with sources.