Over the past several months, our team at Threat Landscape has been tracking a quieter shift in offensive tooling: a wave of open-source frameworks that wrap large language models around reconnaissance, exploitation, and post-exploitation workflows. These projects are marketed as autonomous pentest agents, but many are also being deployed on public VPS instances and used as attack infrastructure. The distinction matters for defenders, because the same defaults that make these tools easy to launch also make them easy to find.
We tested nine of the most visible projects in this category — HexStrike AI, Cairn, CyberStrikeAI, RedAmon, PentAGI, Shannon, Strix, PentestGPT, and ARTEX — using their documented deployment paths. The question we set out to answer was simple:
Question: If one of these frameworks is deployed on a VPS, what listener does it open that defenders can identify?
For each project, we recorded the listening services, default bind addresses, TLS and authentication posture, and the artifacts left on the host and on the wire. This post shares those observations with the defender community so that network defenders, incident responders, and threat intelligence teams can identify and block this infrastructure before it is used against them.
The focus is deliberately defensive. We are publishing defaults, fingerprints, and detection guidance, not exploitation instructions. Where a project is safe by default, we say so; where a default exposes an unauthenticated service, we show what that looks like from the outside. The goal is to shorten the time between a suspicious VPS appearing in your telemetry and someone knowing what it is.
Scope and method
We deployed each project from its public repository using the documented quick-start or compose file, on a clean Linux VPS with no additional hardening. We then inspected listening sockets, process trees, container labels, generated certificates, and outbound traffic. We did not point any of these tools at third-party systems. The matrix below summarizes the default posture; the sections that follow give the specific ports, headers, files, and behaviors that make each project identifiable.
TL;DR matrix
| Project | Primary listener | Default bind | TLS by default | Auth by default | Internet-exposed on a stock VPS? |
|---|---|---|---|---|---|
| HexStrike AI | Flask tools API | 0.0.0.0:8888 (hardcoded) | ❌ plain HTTP | ❌ none at all | 🔴 Yes — unauthenticated RCE panel |
| Cairn | FastAPI server | 0.0.0.0:8000 (compose) | ❌ plain HTTP | ❌ none | 🔴 Yes — full read/write of all projects |
| CyberStrikeAI | Gin web UI/API | 0.0.0.0:8080 | ⚠️ self-signed TLS on by default, CN=CyberStrikeAI | ✅ admin + random password | 🟠 Yes — authenticated panel + login page |
| RedAmon | Next.js UI + FastAPI agent + msf handler | 0.0.0.0:3000, :8090, :4444 | ❌ (TLS via optional nginx overlay) | ⚠️ random keys via installer, changeme fallbacks | 🔴 Yes — incl. reverse-shell catcher on 4444 |
| PentAGI | Go HTTPS API/UI | 127.0.0.1:8443 (compose default) | ✅ self-signed, O=PentAGI | ✅ [email protected]/admin | 🟢 No (loopback) — but ephemeral worker ports 28000–30000 bind 0.0.0.0 |
| Shannon | Temporal server | 127.0.0.1:7233, :8233 | ❌ plain HTTP/gRPC | ❌ none | 🟢 No (loopback only) |
| Strix | Report viewer (optional CLI command) | 127.0.0.1 (ephemeral port; --host 0.0.0.0 opt-in) | ❌ plain HTTP | ⚠️ URL-token cookie | 🟢 No by default; Caido proxy 0.0.0.0:48080 inside sandbox container |
| PentestGPT | None (pure CLI) | n/a | n/a | n/a | 🟢 No inbound service at all |
| ARTEX | Go web console + MITM proxy | 0.0.0.0:8787; MITM proxy 127.0.0.1:8788 (0.0.0.0 in-container) | ❌ plain HTTP (TLS via external nginx) | ⚠️ first-run password setup; JWT; optional Basic Auth | 🔴 Yes — unauthenticated UI/API until first-run setup is done |
Rule of thumb from this dataset: the newer "agent platform" projects (PentAGI, Shannon, Strix, PentestGPT) default to loopback binds or no listener; the "tool-server" projects (HexStrike, Cairn, CyberStrikeAI, ARTEX) and RedAmon bind 0.0.0.0 out of the box and are trivially fingerprintable.
1. HexStrike AI — the worst offender
One listener: Flask tools API on 0.0.0.0:8888, plain HTTP, zero authentication.
- Bind hardcoded:
hexstrike_server.py:17289—app.run(host="0.0.0.0", port=API_PORT). - Port:
hexstrike_server.py:98— envHEXSTRIKE_PORT, default 8888. - Config trap:
HEXSTRIKE_HOST/API_HOST(:99) is defined but never used — it only appears in the startup banner (:198). Setting it to 127.0.0.1 does nothing. - No authentication anywhere across 156 routes, including
POST /api/command(arbitrary command exec,:9137) andPOST /api/python/execute(:14498). Deployed as documented ⇒ internet-reachable unauthenticated RCE. - Runtime secondary listeners: OWASP ZAP daemon on
0.0.0.0:8090viaPOST /api/tools/zap(:14326–14340); ChromeDriver on loopback (no CDP port). mitmproxy code exists but is dormant.
Detection fingerprints
GET /health→{"status":"healthy","message":"HexStrike AI Tools API Server is operational","version":"6.0.0"}plus a ~100-tool inventory (each health check runswhich <tool>×100 — visible subprocess burst).- Outbound UAs:
HexStrike-HTTP-Framework/1.0 (Advanced Security Testing),HexStrike-BrowserAgent/1.0; MCP client uses defaultpython-requestswith a /health retry burst. - Local:
hexstrike.log,/tmp/hexstrike_screenshot_*.png. - No Dockerfile/compose — deployment is bare
python3 hexstrike_server.py, soss -tlnpshows a Werkzeug dev server on 8888 owned by python.
2. Cairn — open FastAPI, no auth
One listener: FastAPI on TCP 8000, plain HTTP, no authentication.
- Compose binds
0.0.0.0:docker-compose.yaml:9("8000:8000"), commandcairn serve --host 0.0.0.0(:23). - Bare-metal CLI default is safer:
127.0.0.1:8000(cairn/src/cairn/cli.py:17-18). - Full unauthenticated read/write/delete of all projects;
/docs,/redoc,/openapi.jsonleft enabled (server/app.py:21-26). - Hidden exposure: dispatcher worker containers run with
network_mode: "host"(dispatcher/runtime/containers.py:51-58) on a Kali image with passwordless sudo — anything an agent opens binds straight onto the VPS host stack. - Dispatcher mounts
/var/run/docker.sock(docker-compose.yaml:31) — root-equivalent host credential.
Detection fingerprints
uvicornserver header +openapi.jsonwith"title":"Cairn","description":"Fact-graph based collaborative exploration protocol","version":"0.2.1".- UI
<title>Cairn</title>; localStorage keyscairn.localPrefs. - Containers
cairn-server/cairn-dispatcher/cairn-dispatch-<project_id>; imageghcr.io/oritera/cairn-worker-container:latest. - Behavior: healthcheck GET
/projectsevery 10s; dispatcher polls/projectsevery 3s. - No TLS anywhere; storage is a single SQLite file (no DB creds exist).
3. CyberStrikeAI — exposed panel + optional C2 farm
Main listener: Gin web UI/API on 0.0.0.0:8080 (internal/config/config.go:2376-2377; config.example.yaml:16-18).
- Auth exists (session + RBAC; login rate-limit 10/min), but the login page and
/api-docsare public. Admin password is randomly generated at first boot and printed to console (internal/security/auth_manager.go:59-89) — no default password. - TLS self-signed by default with CN=
CyberStrikeAI— a high-confidence network fingerprint (internal/app/main_server_tls.go:32-86;config.example.yaml:25,32). - Optional MCP server:
127.0.0.1:8081/mcp, disabled by default; token auto-generated at first boot. - Built-in C2 listeners (runtime, operator-created):
http_beacon/https_beacon(paths/check_in,/tasks,/result,/upload),websocket(/ws, CheckOrigin allow-all),tcp_reverse(magic bytesCSB1, optional unauthenticated raw shell ifallow_legacy_shell: true). Default bind 127.0.0.1 but trivially set to 0.0.0.0. Failed beacon auth returns a disguised generic 404 — that body is itself a fingerprint. - Route surface includes
/api/webshell/execand a host WebSocket terminal/api/terminal/ws.
Detection fingerprints (outbound UAs): CyberStrikeAI/1.7.4 (FOFA/ZoomEye/Quake/Shodan), Mozilla/5.0 (compatible; CyberStrikeAI-WebShell/1.0), cyberstrike-ai/1.0 (LLM calls). Host artifacts: process cyberstrike-ai, config.yaml, data/conversations.db, tmp/c2, beacon curl loop with X-Implant-Token, <title>CyberStrikeAI</title>.
4. RedAmon — exposed by default (0.0.0.0 × 3)
Base docker-compose.yml publishes on all interfaces:
| Port | Service | Citation |
|---|---|---|
| 3000 | Next.js UI (RedAmon title) | docker-compose.yml:1100-1101 |
| 8090 | FastAPI "RedAmon Agent API" v3.0.0 + 4 WebSocket endpoints (/ws/agent, /ws/kali-terminal, …) | :872-873; agentic/Dockerfile:233 (uvicorn --host 0.0.0.0) |
| 4444 | Metasploit reverse-shell catcher (intentionally public so targets connect back) | :768-778 |
- Runtime bonus: a spawned Ollama container publishes 0.0.0.0:11434 via docker-py (
recon_orchestrator/local_llm_manager.py:283-284). - Loopback-only host publishes: 5432 (Postgres), 7474/7687 (Neo4j), 8010 (recon orchestrator), 8000/8002–8005 (MCP SSE servers), 8013/8014 (msf/hydra progress), 4040 (ngrok API), 8015 (tunnel manager), 8016 (terminal PTY WS), 8888 profile
capture(mitmproxy). - Credentials: installer (
redamon.sh:1925-1971) generates keys withopenssl rand -hex 32(fail-closed), but compose fallbacks arechangeme(AUTH_SECRET:-changeme,:1124-1126) and legacy compose files usepostgres/redamon_secret. GVM bootsadmin/adminbefore rotation. MCP tokens are minted with prefixrdmn_mcp_. - The hardened nginx overlay (
tooling/deploy/single-host/) re-binds everything to loopback and closes 4444 — but a baredocker compose uprepublishes 3000/8090/4444 on 0.0.0.0 (documented footgun atcompose/docker-compose.prod.yml:18-21).
Detection fingerprints: <title>RedAmon</title>, OpenAPI title="RedAmon Agent API", /docs+/health exposed, container names prefixed redamon-, networks redamon/pentest-net, X-Orchestrator-Key / rdmn_mcp_ tokens, SSE MCP endpoints at /sse.
5. PentAGI — careful defaults, noisy runtime
- Main UI/API: compose default 127.0.0.1:8443 (HTTPS, self-signed) (
docker-compose.yml:32-33); README tells users to expose it for VPS access. The Go binary standalone defaults to plain HTTP 0.0.0.0:8080 (backend/pkg/config/config.go:79-81). - TLS fingerprint: auto-generated self-signed cert with
O=PentAGI/ CN=localhost / SAN pentagi.local (scripts/entrypoint.sh:20-42) — near-conclusive. - Unauthenticated endpoints:
GET /api/v1/info(returns provider/guest JSON), GraphQL playground, Swagger UI titled "PentAGI Swagger API". - Default creds:
[email protected]/admin(forced password change); scraper basic-authsomeuser/somepass; Langfuse adminP3nTagIsD0d; Neo4jdevpassword; cookie signing salt literallysalt. - Ephemeral exposure: each agent flow spawns a Kali worker container publishing 2 TCP ports in 28000–30000 with host bind
0.0.0.0(backend/pkg/docker/client.go:405-426;DOCKER_PUBLIC_IPdefaults to all interfaces). A VPS running flows sprays listeners across that range. - Beacon: POSTs an install summary (UUID, version, providers) to
update.pentagi.comevery 3h — strong outbound detection signal. - Optional stacks (loopback): Grafana 3000, OTel 4318/8148, Langfuse 4000, Neo4j 7474/7687, Graphiti 8000; loopback pgvector 5432, exporter 9187, scraper 9443. Optional pprof on
:7777if enabled (unauth,/profiler/). - Runs as root with the host Docker socket mounted.
6. Shannon — loopback-only Temporal
- Only listeners: Temporal dev server — gRPC 127.0.0.1:7233 and Web UI 127.0.0.1:8233 (
docker-compose.yml:11-12; identical inapps/cli/infra/compose.yml). No TLS, no auth — but loopback-only, so invisible off-host unless the operator edits the mappings (docs suggest accessing the UI viahttp://<wsl-ip>:8233). - Worker containers publish zero ports (
apps/cli/src/docker.ts:424-528); headless Chromium uses pipe transport, no CDP port. BUT workers run--security-opt seccomp=unconfinedand gethost.docker.internal:host-gateway— from a compromised worker, the host's loopback Temporal (which has no auth) is reachable. - No DB/redis — Temporal uses embedded SQLite. No default credentials at all.
- Fingerprints: container names
shannon-temporal,shannon-worker-<8hex>; labelsshannon.workspace,shannon.task-queue; imageskeygraph/shannon,temporalio/temporal:1.7.0; task queuesshannon-<8hex>; report artifactsSecurity-Assessment-Report.pdf.
7. Strix — no persistent listener by default
- The agent itself never listens. The only host-side server is the optional report viewer:
strix view(or in-TUI launch) — PythonThreadingHTTPServer, default 127.0.0.1 ephemeral port, plain HTTP;--host 0.0.0.0is explicitly supported (strix/interface/viewer/server.py:562-593).- Fingerprint:
Server: StrixViewer/1.0header (server.py:149), cookiestrix_viewer_session_<port>, auth = token in URL query string.
- Fingerprint:
- In-sandbox: Caido MITM proxy binds 0.0.0.0:48080 with
--allow-guestsinside the sandbox container (containers/docker-entrypoint.sh:20,41-47); Docker backend republishes it on host 127.0.0.1 (strix/runtime/docker_client.py:244-247), but withSTRIX_DOCKER_SANDBOX_NETWORKor the e2b backend it's reachable on the container IP with only a guest token. - The sandbox image ships a MITM root CA with fixed subject
CN=Testing Root CA, O=Security Testingand an empty PKCS#12 password — a striking host/IR artifact. - Transient listeners: OAuth callback
127.0.0.1:1455duringstrix auth login chatgpt; payment bridge on random loopback port. - Outbound: telemetry on by default (PostHog
phc_7rO3…, Scarf gateway), OpenRouter attribution headersHTTP-Referer: https://strix.ai,X-Title: Strix; sandbox imageghcr.io/usestrix/strix-sandbox:1.4.0, labelsstrix-run-id.
8. PentestGPT — no listener at all
- Pure CLI. Zero
socket.bind/listen/HTTPServer/Flask/FastAPI in production code; the MCP tool-server runs over stdio only. Verified exhaustively; the only server in-tree is a loopback test fixture. - Deployment-level: container-internal
127.0.0.1:3456(Claude Code Router proxy, not published); transient host 1455/tcp socat relay during one-timemake docker-login. NoEXPOSE, noports:in compose. - Detection is therefore outbound/host-based: SDK-default UAs (
OpenAI/Python,Anthropic/Python) carrying distinctive prompt strings ("You're an excellent cybersecurity penetration tester assistant.", PTT tree template);pentestgptcontainer,pentesteruser with passwordless sudo, volumespentestgpt-claude/pentestgpt-codex/ccr-config;logs/pentestGPT.log;.envwith provider keys;~/.claude/oauth_token.
9. ARTEX — open console + embedded MITM proxy
Go pentest system (module Autumn-27/artex) with embedded Next.js UI, PostgreSQL backend, and a built-in go-mitmproxy traffic recorder.
Main listener: web console + JSON API + SSE on :8787 → 0.0.0.0, plain HTTP.
- Flag default
":8787"— bare:portin Go binds all interfaces (cmd/artex/main.go:55,main.go:117-128). - Compose publishes
"8787:8787"on the host's public interface (docker-compose.yml:31-33). - Auth posture: first run requires
POST /api/auth/initto set the admin password (bcrypt, then 403 forever —server/auth.go:158-207); login user hardcodedARTEX; JWT HS256 with a random persisted key (<basedir>/jwt.key); but until setup is done,GET /api/auth/statusreveals an unconfigured instance and the UI serves a setup page to anyone. Optional HTTP Basic Auth gate (realmRestricted) covers everything including/api/healthif enabled. - ~200 routes under
/api/…: tasks, schedules, assets, traffic, intercept-rules, proxy-pool, workspace file manager, update/rollback, repeater. Real-time is SSE, not WebSocket.
Second listener: embedded MITM proxy (go-mitmproxy v1.9.2) on 8788.
- Bare-metal default
127.0.0.1:8788(cmd/artex/main.go:57; empty disables it), but the Dockerfile CMD overrides to:8788→ 0.0.0.0 in-container (Dockerfile:47); compose republishes it loopback-only on the host (127.0.0.1:8788:8788). - Generates a self-signed MITM CA at
data/traffic/_ca/mitmproxy-ca-cert.pemon first start and injects it into agent shells (SSL_CERT_FILE/CURL_CA_BUNDLE) and Playwright (server/manager.go:557-562) — a freshly trusted go-mitmproxy CA on a host is a strong ARTEX indicator. - Plus an always-on, credential-free loopback forward proxy on a random port (
server/forward_proxy.go:34-44) for hot-swappable egress routing — not remotely reachable.
Support infra: PostgreSQL 16 (network-internal in compose; install.sh local mode publishes 5432 on 0.0.0.0; bench compose uses network_mode: host but is missing its Dockerfile/.env). SQLite for traffic index. DB DSN defaults sslmode=disable. No TLS anywhere in the app — nginx reverse proxy is the documented TLS mode. No gRPC/WebSocket; mcphttp/llmhttp/guard/intercept are outbound/in-process only.
Detection fingerprints
- Routes:
GET /api/health→{"ok":true}(deliberately fingerprint-free),/api/auth/status(reveals unconfigured state), JWT-protected/api/version; cookie/paramartex_token. - Outbound UAs:
artex-selfupdate(GitHub release checks toRuoJi6/ARTEX),artex-egress-probe/1.0,artex-enrich/1.0. - UI title "ARTEX — 自主渗透测试控制台" (LLM-driven autonomous pentest console); ASCII "ARTEX" startup banner.
- Docker: image
ruoji6/artex:latest,EXPOSE 8787 8788, basepython:3.12-slim+ Node 22 + Playwright chromium + nmap; filesdata/traffic/_index/index.sqlite, log prefixes[traffic],[proxy],[auth]. - Docker-compose bench mode (
network_mode: host) would put 8787/8788/5432 directly on the host's public interfaces.
Defender's consolidated detection playbook
Fast network triage on a suspect VPS
ss -tlnp # look for: 8888, 8000, 8080, 3000+8090+4444, 8443, 7233/8233, 48080, 1455, 28000-30000
curl -k -s https://127.0.0.1:8443/api/v1/info # PentAGI
curl -s http://127.0.0.1:8888/health # HexStrike
curl -s http://127.0.0.1:8000/openapi.json | grep Cairn # Cairn
curl -skI https://127.0.0.1:8080 | grep -i subject # CN=CyberStrikeAI
curl -s http://127.0.0.1:8787/api/auth/status # ARTEX (unconfigured = fresh deploy)
docker ps --format '{{.Names}}' | grep -Ei 'pentagi|shannon|strix|redamon|cairn|pentestgpt|cyberstrike|caido|artex'
Highest-signal indicators
- HexStrike /health JSON on 8888 with
version 6.0.0— also means unauthenticated RCE is live. - TLS cert CN=
CyberStrikeAIorO=PentAGI/ SANpentagi.local— self-signed branding. Server: StrixViewer/1.0header;Cairnopenapi.json title.- TCP 4444 with msf handler + 3000/8090 together = RedAmon.
- Ephemeral listener clusters in 28000–30000 appearing during runs = PentAGI workers.
- Outbound beacons:
update.pentagi.com(3h), PostHog/Scarf (strix-cli), LLM UA stringsCyberStrikeAI/…,HexStrike-…/1.0,artex-selfupdate/artex-egress-probe/1.0/artex-enrich/1.0,HTTP-Referer: https://strix.ai. - Host artifacts: MITM CA
CN=Testing Root CA, O=Security Testingin trust stores (Strix); freshly generated go-mitmproxy CA atdata/traffic/_ca/(ARTEX); processeshexstrike_server.py,caido-cli --allow-guests,cyberstrike-ai,strix,pentestgpt; docker labelsstrix-run-id,shannon.workspace; imageruoji6/artexwith exposed 8787/8788.
Risk ranking (exposure if deployed on a public VPS)
- 🔴 HexStrike AI — 0.0.0.0:8888, no auth, RCE endpoints. The
HEXSTRIKE_HOSTsetting silently does nothing. - 🔴 RedAmon — UI + agent API + msf catcher on 0.0.0.0 unless the nginx overlay is used correctly.
- 🔴 Cairn — 0.0.0.0:8000 with no auth; host-networked Kali workers compound it.
- 🔴 ARTEX — 0.0.0.0:8787 plain HTTP; unconfigured instances leak a setup page to anyone; MITM CA generation is a loud host-side indicator.
- 🟠 CyberStrikeAI — exposed but authenticated; self-signed TLS branding; operator can add internet-facing C2 listeners (incl. an unauth raw-shell mode).
- 🟢 PentAGI — loopback by default, but worker ports leak onto 0.0.0.0 and the README pushes operators to expose 8443.
- 🟢 Shannon — loopback-only; risk is operator edits + auth-less Temporal reachable from seccomp-unconfined workers.
- 🟢 Strix — nothing persistent; exposure requires explicit
strix view --host 0.0.0.0. - 🟢 PentestGPT — no inbound service by design.
What this means for defenders
The common thread is not sophistication; it is convenience. Projects that bind to 0.0.0.0 and skip authentication do so because it makes the first run easier, and that convenience survives into production when operators follow the README on a public VPS. For defenders, the practical response is to treat these ports and fingerprints as a watchlist, not a blocklist of product names. Attackers rename containers, change ports, and put reverse proxies in front of panels. The durable signals are the ones that are expensive to change: default TLS subjects, API response schemas, user-agent strings, host artifacts, and the shape of outbound beacon traffic.
We recommend three steps. First, scan your own external estate for the default ports and TLS subjects in this report, then verify whether any instance is intentionally exposed. Second, add the highest-signal network and host indicators above to detection engineering, with alerting on new listeners in the ephemeral ranges and on the outbound beacons described above. Third, if you discover an exposed instance that is not yours, preserve what you can and report it through your normal abuse or law-enforcement channel rather than interacting with it.
We are sharing this research because the defender community sees the same infrastructure from the outside, often without context. If your team has additional fingerprints or corrections, we would like to hear them.
This research is provided for defensive purposes. It describes default configurations and observable artifacts only; it is not a guide to deploying or operating these tools against third parties.