Commands and Hooks
Command data
When a user enters a command in the agent console, it is passed to the Commander handler. This handler matches the entered command with the registered AxCommand object, and then parses and converts the commandline into a JSON object:
The argument name is the JSON key and the value is the JSON value.
The JSON value type will match the argument type. The exception is the FILE type, as the file will be read and encoded in base64.
Example:
Registered command:
let _cmd_start = ax.create_command("start", "Description");
_cmd_start.addArgBool("-b");
_cmd_start.addArgInt("arg_int", true);
_cmd_start.addArgString("arg_str", true);
_cmd_start.addArgFlagString("-flag", "arg_str_flag", true);
_cmd_start.addArgFile("content", true);
let _cmd_stop = ax.create_command("stop", "Description");
_socks_stop.addArgInt("i", true);
let cmd = ax.create_command("cmd", "Description");
cmd.addSubCommands([_cmd_start, _cmd_stop]);
CommandLine:
cmd start -b 123 test -flag test_flag /tmp/file
JSON:
{
"command": "cmd",
"subcommand": "start",
"-b": true,
"arg_int": 123,
"arg_str": "test",
"arg_str_flag": "test_flag",
"content": "dGVzdCBjb250ZW50"
}
PreHook
You can set a PreHook
for a command. PreHook
is an AxScript function that will process the command data between the command entry and sending its data to the server.
setPreHook(function() handler)
handler(id, cmdline, parsed_json, ...parsed_lines)
- is an AxScript function that takes 4 parameters:
id
- identifier of the agent for which the command is specifiedcmdline
- command sent from the agent consoleparsed_json
- the JSON object that the command was converted to after processing.parsed_lines
- is an array of strings that the command was converted to.
Example
This code creates the screenshot_bof
command
var cmd_screenshot = ax.create_command("screenshot_bof", "Alternative screenshot capability that does not do fork n run by @codex_tf2", "screenshot -n screen1 -p 812");
cmd_screenshot.addArgFlagString("-n", "note", "Screenshot caption", "ScreenshotBOF");
cmd_screenshot.addArgFlagInt("-p", "pid", "PID of the application whose window screenshot will be taken. If 0, then a full-screen screenshot", 0);
If the command is sent from the agent console: screenshot_bof -n "Test bof" -p 608
cmdline:
screenshot_bof -n "Test bof" -p 608
parsed_lines:
["screenshot_bof", "-n", "Test bof", "-p", "608"]
parsed_json:
{
"command": "screenshot_bof",
"note": "Test bof",
"pid": 608
}
In this example, setPreHook
installs a handler that packages arguments into BOF format and executes the execute bof
command via the ax.execute_alias
function.
cmd_screenshot.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines) {
let note = parsed_json["note"];
let pid = parsed_json["pid"];
let bof_params = ax.bof_pack("cstr,int", [note, pid]);
let bof_path = ax.script_dir() + "_bin/Screenshot." + ax.arch(id) + ".o";
ax.execute_alias(id, cmdline, `execute bof ${bof_path} ${bof_params}`, "Task: Screenshot BOF");
}
PostHook
A PostHook is used to allow the user to get access to the result and do additional processing on the information. AxScript uses the concept of hooks because of the asynchronous behavior of sending a task to beacon and the response being received sometime in the future based on the current sleep time.
Once your asynchronous hook is executed you can then perform the necessary operations to process the result for your use case. Hooks can be used to process the result of a task, run an additional task, or format the result before output it in the agent console.
PostHook can be set via the ax.execute_alias
or ax.execute_command()
functions.
hooktask handler(hooktask task)
- is an AxScript function that takes hooktask
parameter and return hooktask
.
task
- ahooktask
structure that contains the result of the task execution.
// HOOKTASK STRUCTURE:
string hook_task["agent"] // agent ID
string hook_task["type"] // message type -> "info", "error" or "success"
string hook_task["message"] // console
string hook_task["text"] // task output
bool hook_task["completed"] // is the task complete (true) or is it still running (false)
int hook_task["index"] // job_index
Both the client and teamserver save requests that have associated callbacks in a queue. The request is deleted when the initiating client disconnects from TeamServer. This deletes the queue managed by the client, as it is tied to each TeamServer connection. The queue on the teamserver will see the originating client has disconnected and flag any requests for that client to be removed. This means the originating client needs to stay connected to the teamserver until the command with a PostHook has completed. Otherwise, any responses from Beacon after a disconnection from the originating client will be lost.
Example
Hashdump BOF output without posthook:

This posthook removes "BOF output" messages, parses names and hashes, and stores them in Credentials Manager.
let hook = function (task)
{
let agent = ax.agents()[task.agent];
let computer = agent["computer"];
let address = agent["internal_ip"];
let match;
let regex = /^([a-zA-Z0-9_\-]+):\d+:([a-fA-F0-9]{32})$/gm;
while ((match = regex.exec(task.text)) !== null) {
ax.credentials_add(match[1], match[2], "", "ntlm", "", "SAM", `${computer} (${address})`);
}
if(task.message != "BOF finished" && task.index != 0) { task.message = ""; }
return task;
}


AxScript Execute command
execute_command
The function passes the specified command
to the Commander handler for the agent with the specified id
. This will work as if the user entered the command in the agent console.
void ax.execute_command(string id, string command, handler(task){} = nil);
id
- agent IDcommand
- command linehandler
- PostHook
Example:
ax.execute_command(id, "screenshot");
execute_browser
The function passes the specified command
to the Commander handler for the agent with the specified id
, but marks the task as TYPE_BROWSER, so the command and its result are not saved and displayed in the agent console.
void ax.execute_browser(string id, string command);
id
- agent IDcommand
- command line
Example:
ax.execute_browser(id, "disks");
execute_alias
The function passes the specified command
to the Commander handler, but overrides the comandline
and message
for the agent console with the specified id
. So if execute_command
outputs the original comandline
and message
, execute_alias
will replace them after processing and before output to the agent console.
void execute_alias(string id, string cmdline, string command, string message = "", handler(task){} = nil);
id
- agent IDcommand
- command linecmdline
- new command line for agent consolemessage
- new message for agent consolehandler
- PostHook
Example:
var _cmd_getsystem_token = ax.create_command("token", "Elevate the current agent to SYSTEM and gain the TrustedInstaller group privilege through impersonation", "getsystem token");
_cmd_getsystem_token.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines) {
let hook = function (task)
{
if(/Impersonate to SYSTEM & TrustedInstaller succeeded/.test(task.text)) {
ax.agent_set_impersonate(task.agent, "SYSTEM", true);
}
return task;
}
let bof_path = ax.script_dir() + "_bin/getsystem_token." + ax.arch(id) + ".o";
ax.execute_alias(id, cmdline, `execute bof ${bof_path}`, "Task: Get system via token (BOF)", hook);
});
var cmd_getsystem = ax.create_command("getsystem", "Elevate context to SYSTEM");
cmd_getsystem.addSubCommands([_cmd_getsystem_token]);
Last updated