Tools modules execute directly from the teamserver against remote services without deploying an agent.
Interface
#Tools modules use AgentlessModuleBase with class-level metadata and Command class attributes for operations. The base class auto-discovers commands and dispatches execute() to handle_<operation> methods — no metadata() or execute() override needed.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| from tantoc2.server.agentless_base import (
AgentlessModuleBase,
AgentlessTarget,
AgentlessResult,
)
from tantoc2.server.command_schema import Command
class MyToolModule(AgentlessModuleBase):
name = "my_service"
description = "Interact with MyService"
author = "Your Name"
protocol = "my_protocol"
mitre_attack = ["T1021"]
supports_shell = False
dependencies = ["some-lib>=1.0"]
exec = (
Command("exec", "Execute a command on the target")
.arg("command", type="str", desc="Command to run", required=True)
)
def handle_exec(
self,
targets: list[AgentlessTarget],
options: dict,
*,
credentials: dict | None = None,
proxy: dict | None = None,
) -> list[AgentlessResult]:
results = []
for target in targets:
try:
# ... connect, execute, return result ...
results.append(AgentlessResult(target=target, success=True, data={"output": "..."}))
except Exception as exc:
results.append(AgentlessResult(target=target, success=False, error=str(exc)))
return results
|
See Building Tool Plugins for the full step-by-step guide including credential handling, proxy support, and tests.
AgentlessTarget
#1
2
3
4
5
| @dataclass
class AgentlessTarget:
host: str
port: int | None = None
credential_id: str | None = None # references credential store
|
AgentlessResult
#1
2
3
4
5
6
7
8
| @dataclass
class AgentlessResult:
target: AgentlessTarget
success: bool
data: dict[str, Any] = field(default_factory=dict)
credentials: list[ExtractedCredential] = field(default_factory=list)
error: str | None = None
raw_output: str | None = None
|
Return one AgentlessResult per target — even on failure. Never raise from a handler.
Return ExtractedCredential objects in credentials to auto-populate the engagement’s credential store with anything the module discovers.
Key Concepts
#Credential Integration
#When targets have a credential_id, the manager decrypts and passes credentials to handle_<operation> via the credentials dict (credential_id → {"username", "secret", "cred_type", "domain", ...}). Always check if credentials is None before subscripting.
Proxy Support
#When a proxy is configured, a dict is passed via the proxy parameter containing proxy_type, host, port, and optionally username. Use PySocks to route connections. Declare PySocks as an optional dependency.
Multi-Target Execution
#handle_<operation> receives all targets as a list. Modules handle their own parallelism. Results must be returned in the same order as the input targets.
Deployment
#Standalone Package (Recommended)
#1
2
| [project.entry-points."tantoc2.agentless_modules"]
my_tool = "tantoc2_tool_mytool.module:MyToolModule"
|
1
| pip install ./my-tool-package
|
File Drop
#1
2
| cp my_tool.py tantoc2/plugins/agentless/
tantoc2> tools refresh
|