Files
2026-05-10 18:31:47 +02:00

158 lines
5.8 KiB
Python

from __future__ import annotations
import pytest
from C2Client.assistant_agent.tools.command_builder import build_command_line
from C2Client.assistant_agent.tools.command_specs import command_spec_to_tool_spec
from helpers import arg, command_spec
def tool_spec(command):
return command_spec_to_tool_spec(command)
def test_build_command_line_quotes_paths_with_spaces():
cat = tool_spec(command_spec("cat", "cat {path:q}", [arg("path", arg_type="path", required=True)]))
ls = tool_spec(command_spec("ls", "ls {path:q?}", [arg("path", arg_type="path")]))
assert build_command_line(cat, {"beacon_hash": "b", "listener_hash": "l", "path": "C:\\Users\\Public\\notes.txt"}) == "cat C:\\Users\\Public\\notes.txt"
assert build_command_line(ls, {"beacon_hash": "b", "listener_hash": "l", "path": "C:\\Program Files"}) == "ls 'C:\\Program Files'"
def test_build_command_line_supports_raw_command_tail():
run = tool_spec(command_spec("run", "run {command:raw}", [arg("command", required=True, variadic=True)]))
assert build_command_line(
run,
{"beacon_hash": "b", "listener_hash": "l", "command": "whoami /all"},
) == "run whoami /all"
def test_build_command_line_omits_empty_optional_argument():
enumerate_shares = tool_spec(command_spec("enumerateShares", "enumerateShares {host:q?}", [arg("host")]))
ls = tool_spec(command_spec("ls", "ls {path:q?}", [arg("path", arg_type="path")]))
assert build_command_line(
enumerate_shares,
{"beacon_hash": "b", "listener_hash": "l", "host": ""},
) == "enumerateShares"
assert build_command_line(
ls,
{"beacon_hash": "b", "listener_hash": "l"},
) == "ls"
def test_build_command_line_supports_optional_flag_segments():
dcom_exec = tool_spec(
command_spec(
"dcomExec",
"dcomExec -h {h:q} -c {c:q} [-a {a:q}] [-n {n:flag}]",
[
arg("-h", required=True),
arg("-c", required=True),
arg("-a"),
arg("-n"),
],
)
)
assert build_command_line(
dcom_exec,
{
"beacon_hash": "b",
"listener_hash": "l",
"h": "host1",
"c": "cmd.exe",
"a": "/c whoami",
"n": True,
},
) == "dcomExec -h host1 -c cmd.exe -a '/c whoami' -n"
def test_build_command_line_rejects_missing_required_argument():
cat = tool_spec(command_spec("cat", "cat {path:q}", [arg("path", arg_type="path", required=True)]))
with pytest.raises(KeyError):
build_command_line(cat, {"beacon_hash": "b", "listener_hash": "l"})
@pytest.mark.parametrize(
("command", "arguments", "expected"),
[
(
command_spec(
"assemblyExec",
"assemblyExec [--mode {mode}] [--donut-exe {donut_exe:q}] [--method {method:q}] [-- {arguments:raw}]",
[
arg("--mode", values=["thread", "process"]),
arg("--donut-exe", artifact=True),
arg("--method"),
arg("arguments", variadic=True),
],
),
{"mode": "process", "donut_exe": "Rubeus.exe", "arguments": "triage"},
"assemblyExec --mode process --donut-exe Rubeus.exe -- triage",
),
(
command_spec(
"inject",
"inject --pid {pid} [--donut-exe {donut_exe:q}] [-- {arguments:raw}]",
[
arg("--pid", arg_type="number", required=True),
arg("--donut-exe", artifact=True),
arg("arguments", variadic=True),
],
),
{"pid": -1, "donut_exe": "BeaconHttp.exe", "arguments": "arg1 arg2"},
"inject --pid -1 --donut-exe BeaconHttp.exe -- arg1 arg2",
),
(
command_spec(
"registry",
"registry {operation} -h {h:q} -k {k:q} [-n {n:q}]",
[
arg("operation", values=["query", "set"], required=True),
arg("-h", required=True),
arg("-k", required=True),
arg("-n"),
],
),
{"operation": "query", "h": "HKCU", "k": "Software\\C2", "n": "Smoke"},
"registry query -h HKCU -k Software\\C2 -n Smoke",
),
(
command_spec(
"spawnAs",
"spawnAs [--no-profile {no_profile:flag}] {username:q} {password:q} -- {command:raw}",
[
arg("--no-profile"),
arg("username", required=True),
arg("password", required=True),
arg("command", required=True, variadic=True),
],
),
{"no_profile": True, "username": ".\\c2test", "password": "pw", "command": "cmd.exe /c whoami"},
"spawnAs --no-profile .\\c2test pw -- cmd.exe /c whoami",
),
(
command_spec(
"sshExec",
"sshExec -h {h:q} [-P {P}] -u {u:q} -p {p:q} -- {command:raw}",
[
arg("-h", required=True),
arg("-P", arg_type="number"),
arg("-u", required=True),
arg("-p", required=True),
arg("command", required=True, variadic=True),
],
),
{"h": "server", "P": 2222, "u": "admin", "p": "pw", "command": "/bin/id"},
"sshExec -h server -P 2222 -u admin -p pw -- /bin/id",
),
],
)
def test_build_command_lines_from_command_spec_templates(command, arguments, expected):
arguments = {"beacon_hash": "b", "listener_hash": "l", **arguments}
assert build_command_line(tool_spec(command), arguments) == expected