Agentic pentest orchestration: PTT and CrewAI¶
Scope: how autonomous pentest tools structure multi-step work over MCP-wired or library-wrapped security tools, compared through two open implementations. GHOSTCREW (0xSojalSec/PentestAgent, MIT) drives a dynamic Pentesting Task Tree (PTT) with an MCP and RAG architecture. TARS (osgil-defense/TARS, MIT, archived) drives a fixed sequential CrewAI pipeline of role agents. This page covers both control models, their completion and isolation semantics, and when a dynamic tree beats a deterministic pipeline. It sits in the agentic cybersecurity and SysOps index with the tool-executing engines ptai and Shannon, and it is a security-domain application of orchestration and control plane and hierarchical agent decomposition.
GHOSTCREW examined at commit
b642fa90of0xSojalSec/PentestAgent(the repository is a mirror; the project brands itself GHOSTCREW, upstreamGH05TCREW/ghostcrew), MIT. TARS examined at commit6cc646bbofosgil-defense/TARS, MIT, archived (read-only since January 2025, pinned tocrewai==0.28.6andlangchain==0.1.16). The two control models below were executed and asserted as standalone Python. No pentest was run: both tools execute real security tools (nmap, Metasploit, ZAP, RustScan) against targets and need an LLM key, so capability claims are read from the code, not measured here.
What they are¶
Both tools give an LLM a set of security tools and a way to sequence multi-step work. They differ in the control model.
GHOSTCREW is an AI red-team assistant with three modes: chat, predefined workflows, and an autonomous agent mode built on a Pentesting Task Tree. In agent mode (core/pentest_agent.py, core/task_tree_manager.py, core/ptt_reasoning.py), the LLM expands a goal into a tree of subtasks, decides which to pursue, and updates node status as it goes; the tree is the strategic memory that lets it reason about what is done, what is blocked, and what to do next. Tools reach it over MCP (nmap, Metasploit, ffuf, Amass, Katana, httpx, and more), and a local RAG knowledge base can enhance responses.
TARS is an archived research project that automates parts of pentesting with CrewAI. Its agents (tars/agents.py) are role-typed (Web Application Penetration Tester, Network Penetration Tester, plus researcher/writer/markdown agents) with tools wrapping RustScan, ZAP, Nettacker, and OWASP tooling. Its crews (tars/crews.py) run Process.sequential: a fixed order where each agent consumes the previous agent's output, ending in a Markdown report. It runs behind a Streamlit UI.
Why the control model matters¶
- A tree adapts; a pipeline is predictable. PTT lets the agent discover that an SSRF path opened up a new internal host and spawn subtasks for it mid-run. A sequential crew always runs the same roles in the same order, which is easier to reason about and audit but cannot react to what it finds.
- Isolation of failure. In a well-formed PTT, a blocked branch (target unreachable, out of scope, tool failed) halts only that branch; sibling branches keep working. In a strict sequential pipeline, a failed stage tends to starve every downstream stage.
- Completion semantics. PTT propagates status bottom-up: a parent is complete only when all children complete, and blocked wins over in-progress. A sequential crew is complete when the last stage returns.
- Auditability versus autonomy. The deterministic pipeline is the safer default for a first deployment because its action set is bounded; the tree is more capable but has a larger, data-dependent action surface that needs a tighter scope guard.
When to use which (and when not)¶
Reach for a PTT-style dynamic tree when the engagement is open-ended and the interesting paths are discovered during the run, and when you have a solid scope guard and human approval gate around the larger action surface. Reach for a sequential crew when you want a repeatable, auditable pass over a known method (recon, then scan, then report) and predictability matters more than adaptivity.
Do not deploy TARS as-is: it is archived, pinned to a 2024 CrewAI and LangChain stack with known-old transitive dependencies, and was never more than a research prototype ("our attempt towards trying to automate parts of cybersecurity penetration testing"). Do not treat either tool's autonomous mode as safe without a scope guard: both can invoke real, intrusive tools, and neither ships the machine-verification gate that ptai uses, so their findings are LLM-asserted, not proven. Do not run either against anything you are not authorized to test.
Architecture¶
flowchart TB
subgraph PTTMODE["GHOSTCREW: dynamic PTT"]
GOAL["Goal"] --> TREE["Task tree (LLM-expanded)"]
TREE --> NODE["Node: pursue / block / complete"]
NODE --> MCP["MCP tools: nmap, Metasploit, ffuf"]
MCP --> TREE
end
subgraph CREWMODE["TARS: sequential CrewAI"]
IN["Target"] --> A1["Web/Network pentester agent"]
A1 --> A2["Researcher agent"]
A2 --> A3["Writer + Markdown agent"]
A3 --> OUT["Report"]
end
Both control models, executed¶
The model below reproduces PTT status propagation (bottom-up, blocked-wins, blocked-branch isolation) and the CrewAI sequential hand-off, and asserts the completion semantics of each.
# ---- PTT: bottom-up status propagation over a task tree --------------------
def ptt_status(node):
"""A node is complete iff no unfinished descendants; blocked wins."""
kids = node.get("children", [])
if not kids:
return node["status"] # leaf: its own status
child_states = [ptt_status(k) for k in kids]
if any(s == "blocked" for s in child_states):
return "blocked" # a blocked child blocks its parent
if all(s == "complete" for s in child_states):
return "complete"
return "in_progress"
tree = {"name": "own the app", "children": [
{"name": "recon", "status": "complete", "children": [
{"name": "subdomains", "status": "complete"},
{"name": "port scan", "status": "complete"},
]},
{"name": "exploit", "children": [
{"name": "sqli", "status": "complete"},
{"name": "ssrf", "status": "in_progress"},
]},
]}
assert ptt_status(tree) == "in_progress" # one leaf still running
print("PTT root while ssrf runs:", ptt_status(tree))
tree["children"][1]["children"][1]["status"] = "complete"
assert ptt_status(tree) == "complete"
print("PTT root once ssrf done :", ptt_status(tree))
# A blocked leaf (target unreachable, out of scope) blocks only its branch.
tree["children"][1]["children"][1]["status"] = "blocked"
assert ptt_status(tree["children"][0]) == "complete" # recon branch unaffected
assert ptt_status(tree) == "blocked"
print("PTT recon branch:", ptt_status(tree["children"][0]),
"| root:", ptt_status(tree), "(blocked branch does not sink siblings)")
# ---- CrewAI sequential: fixed pipeline, output threads forward --------------
def crew_sequential(agents, task_input):
ctx, trace = task_input, []
for name, fn in agents:
ctx = fn(ctx)
trace.append((name, ctx))
return ctx, trace
agents = [
("researcher", lambda x: x + " -> findings"),
("writer", lambda x: x + " -> draft"),
("markdown", lambda x: x + " -> report.md"),
]
final, trace = crew_sequential(agents, "scan results")
assert [t[0] for t in trace] == ["researcher", "writer", "markdown"]
assert final.endswith("report.md")
print("CrewAI order:", " -> ".join(t[0] for t in trace))
print("OK: PTT gives dynamic tree autonomy with blocked-branch isolation; CrewAI "
"sequential gives deterministic hand-offs. Same tools, different control models")
Executed output:
PTT root while ssrf runs: in_progress
PTT root once ssrf done : complete
PTT recon branch: complete | root: blocked (blocked branch does not sink siblings)
CrewAI order: researcher -> writer -> markdown
OK: PTT gives dynamic tree autonomy with blocked-branch isolation; CrewAI sequential gives deterministic hand-offs. Same tools, different control models
How to use them¶
GHOSTCREW: clone the upstream (GH05TCREW/ghostcrew), create a venv, pip install -r requirements.txt, install Node.js and uv for the MCP tool servers, then python main.py. It prompts you to configure MCP tools (stored in mcp.json), optionally load a knowledge base from the knowledge folder, and pick Chat, Workflows, or Agent mode. Agent mode is the PTT-driven autonomous path; it saves the PTT state and generates a Markdown report.
TARS: install Docker, create a .env from .template_env with your API keys, then bash cli.sh -r to build and launch the Streamlit UI (typically http://localhost:8501/). Because it is archived and pinned to old dependencies, build it in a disposable container and expect version friction.
How to develop with them¶
GHOSTCREW's PTT lives in core/task_tree_manager.py (tree structure and operations) and core/ptt_reasoning.py (LLM-driven task management); new tool integrations are MCP servers registered in mcp.json. TARS's agents, tools, and crews are the extension points (tars/agents.py, tars/tools/, tars/crews.py); a new capability is a new CrewAI tool plus an agent that holds it, wired into a sequential crew. For new work, prefer the PTT pattern on a current agent framework over reviving the archived CrewAI 0.28 stack.
How to maintain them¶
GHOSTCREW is active; pin mcp.json tool versions and the commit, since the MCP tool surface is where behavior changes. TARS is archived and will not receive fixes: its requirements.txt pins crewai==0.28.6, langchain==0.1.16, and a 2024 transitive set, so treat it as a reference implementation of the sequential pattern rather than something to run in a maintained environment.
How to run them in production¶
Neither is a hardened product. If you run GHOSTCREW's autonomous mode, wrap it in a scope guard as strict as the pentest-ai-agents guard, because a dynamic PTT has a data-dependent action surface, and route its MCP tool egress through runtime enforcement and a sandbox. A sequential crew is easier to bound but still executes intrusive tools; gate any mutating action behind human approval. Whichever control model you pick, remember that both emit LLM-asserted findings: pair them with a verification step before acting on a result.
Failure modes¶
- Unverified findings. Both tools report what the LLM concludes from tool output; neither machine-proves an exploit. A confident writeup can be wrong.
- Dynamic action surface (PTT). A tree that expands from tool output can wander if the scope guard is weak; the blocked-branch isolation helps availability, not authorization.
- Archived stack (TARS). Old CrewAI and LangChain pins carry their own transitive-dependency risk and no fixes; the project is a prototype by its authors' own description.
- MCP tool trust. GHOSTCREW's power comes from wrapped tools reached over MCP; a misconfigured or malicious tool server is inside the agent's trust boundary.
- No scope guard by default. Unlike the prompt-only subagents, these engines do not ship a hard-refusal list; you must impose scope and approval yourself.
References¶
- GHOSTCREW / PentestAgent (mirror), pinned commit
b642fa90: https://github.com/0xSojalSec/PentestAgent - GHOSTCREW upstream: https://github.com/GH05TCREW/ghostcrew
- TARS repository (osgil-defense, archived), pinned commit
6cc646bb: https://github.com/osgil-defense/TARS - CrewAI (the framework TARS uses): https://github.com/crewAIInc/crewAI
- Model Context Protocol: https://modelcontextprotocol.io
Related: Agentic cybersecurity and SysOps index · Earned-verdict pentesting with ptai · Autonomous web pentesting with Shannon · Orchestration and control plane · Hierarchical agent decomposition · Claude Code pentest subagents