Skip to content
xcoy0te
Go back

Testing My Claude-ExternalPentest Plugin

I gave it one thing: a single in-scope IP. Everything after that, mapping the attack surface, fingerprinting the one exposed service, and matching it against a vulnerability that had been disclosed as an actively-exploited zero-day that same day, the agents did on their own. This is a walkthrough of that run. Client data is redacted throughout; the interesting part isn’t who the target was, it’s how little I had to drive.

The plugin

The tool is Claude-ExternalPentest — a Claude Code plugin that runs a methodology-driven external test through the same phases a human would, aligned to PTES and the OWASP WSTG. Five commands, each backed by a focused sub-agent with scoped tool access:

StepCommandWhat runs
Scope/ext-scope <targets>Records allow/deny lists, windows, rate limits, forbidden actions
Recon + enum/ext-reconext-enumerator: OSINT → discovery → web/TLS/DNS → services → nuclei. Read-only
Attack plan/ext-attack-pathsext-attack-planner: correlate exposures → ranked footholds by CVSS. Plan only
Exploit/ext-exploitext-exploit-operator: one approved step at a time, confirms before anything intrusive
Report/ext-reportext-reporter: severity-ranked findings + evidence + remediation

Every phase reads ./ext-engagement/scope.md and refuses to act outside it. That constraint is the whole reason this is safe to point at a production gateway.

1 · Scope — the only thing I typed

The entire manual input for the engagement was one line naming the in-scope IP:

Claude Code running the ext-scope command: it performs a light reachability check, writes scope.md, and prints the recorded allowlist, denylist, and forbidden actions

From that one line the agent wrote a full scope.md: a single-host allowlist (“exact IP only”), a default-deny denylist that explicitly rules out the VPN concentrator, CDN/WAF edge, other tenants, and any off-target reverse-DNS hostname, plus a forbidden-actions list covering DoS, disruptive exploitation, phishing, lockout-risk brute force, and touching shared upstream infra.

In scope (allowlist)
- <redacted IP> — the single authorized host, exact IP only   # [!code highlight]

Out of scope (denylist) — must not be probed
- Anything other than <redacted IP>
- VPN concentrator, cloud provider consoles, CDN/WAF edge, other tenants
- Any off-target reverse-DNS hostname or vhost
- Default-deny: any target matching neither list is out of scope.
  No CIDR expansion, no pivoting, no subdomain/vhost testing.

Forbidden actions
- No DoS / stress / availability-impacting testing
- No exploitation that could disrupt production
- No password spraying / brute force that risks lockout
- No destructive payloads

BTW, the scope message itself tripped a real-time cyber safeguard flag before proceeding. It didn’t block the work, the run continued, in-scope, exactly as written, but if you do this kind of thing you’ll see the flag, and it’s worth knowing it’s expected rather than a sign something’s wrong.

2 · Recon — read-only, 19 minutes, one port

/ext-recon handed off to the ext-enumerator agent. No approval needed — the entire phase is read-only by design. Nineteen minutes later it came back with a tight surface:

Recon agent summary: one open port (443/tcp), SonicWall SMA 1000 WorkPlace SSL-VPN portal running SMA/12.5, solid TLS, zero nuclei matches, and a flag that the exact firmware build is unknown

The whole attack surface was one port:

Scope discipline held here too, the TLS cert revealed an off-target hostname, which the agent recorded as data only and never resolved or probed, because it wasn’t on the allowlist.

The commands were standard, rate-limited nmap, all logged to ext-engagement/recon/ for the report appendix:

# host liveness — ICMP filtered, confirmed via TCP SYN/ACK
nmap -sn -PS22,80,443,3389,8080,8443 -PA80,443 -T3 <target>

# full TCP sweep
nmap -Pn -T3 -p- --min-rate 300 --max-retries 2 <target>
# → 443/tcp open  https   (65,534 filtered)

# service / version detection
nmap -Pn -T3 -sV -sC --version-all -p 443 <target>
# → 443/tcp open  ssl/https  SMA/12.5   

The single most important line in the whole recon phase is the one the agent flagged as unconfirmed: the banner exposes 12.5, but not the exact 12.5.0-NNNNN patch level. It refused to assume vulnerability from the banner alone.

3 · Attack planning — correlating a one-port surface against a same-day advisory

/ext-attack-paths launched the ext-attack-planner to correlate that confirmed SMA 1000 / WorkPlace 12.5 surface against known vulnerabilities — precise about SMA 1000 vs SMA 100 (different product lines), ranking footholds by prerequisites, intrusiveness, and appliance-crash risk. Plan only, no exploitation.

The ext-attack-planner launching as a backgrounded agent to correlate the SonicWall SMA 12.5 surface against known vulnerabilities and rank candidate footholds

Here’s where the surface and the calendar collided. On the same day the target was being assessed, SonicWall published SNWLID-2026-0016: two SMA 1000 zero-days, confirmed actively exploited in the wild, in the exact component sitting on this host’s 443:

Chained, they move an attacker from unauthenticated external access to arbitrary OS command execution. The planner pulled the ranked footholds together and separated what applied from what didn’t:

Ranked-footholds table from the attack plan: CVE-2026-83548 pre-auth SSRF as the top foothold, CVE-2026-83549 as the RCE half of the chain, weak creds as low-value, and CVE-2025-23006 excluded as not applicable to 12.5

The discipline in that table is the part I care about. It didn’t just grab every SonicWall CVE it could find. It excluded CVE-2025-23006 as not applicable (that one hits ≤12.4.3; the target is 12.5), it noted credential spraying was forbidden by scope regardless of whether it might work, and it pinned the entire question to one unknown:

The banner shows only SMA/12.5 — not the exact 12.5.0-NNNNN build. That build is the single fact that decides vulnerable vs patched. Vulnerable at ≤ 12.5.0-02835, fixed at ≥ 12.5.0-02952.

So the recommended first move was not an exploit. It was a safe, non-intrusive build confirmation, read-only version/metadata endpoints, asset hashes, error-page build strings, to resolve vulnerable-vs-patched without touching auth, without lockout risk, without availability impact. Anything intrusive after that would stop and wait for approval.

4 · The finding, and where it stops

This is the responsible shape of the finding, and it’s a stronger result than a bare “exploited it”:

Two things still gated any intrusive step, and the agent named both rather than pushing past them: a confirmed testing window + rollback plan (not yet in scope.md, and required before touching a production VPN gateway), and client notification given active in-the-wild exploitation. No exploitation approval, no intrusive step. It asked first.

5 · The report

/ext-report turned the engagement artifacts into a client-ready document, executive summary, the single-service finding, and a full command appendix with raw output so everything is reproducible:

External Penetration Test report cover and executive summary describing the single exposed SonicWall SMA 1000 WorkPlace SSL-VPN service on TCP/443 and the 12.5 firmware line

Report Appendix A listing the exact rate-limited, non-destructive nmap commands run against the in-scope host and their output

Why this is the interesting part

I want to be clear about what did and didn’t happen. I didn’t feed it a CVE to look for. I fed it an IP. The agents fingerprinted the surface down to a specific product and version line, then went and found which freshly-disclosed advisories were relevant to that surface, including one published the same day. The attack surface drove the vuln research, not the other way around. That’s the difference between a scanner running yesterday’s template set and an agent that reasons about what a SMA/12.5 banner means the day a matching 10.0 drops.

And it held the line in three places that matter on a real engagement: it stayed on the single in-scope IP, it never assumed exploitability from a banner, and it stopped at the boundary between read-only detection and intrusive proof-of-concept, surfacing the decision to me instead of making it.

The plugin is on GitHub — xcoy0te/Claude-ExternalPentest. It’s Markdown and a small manifest, so it’s easy to read exactly what each agent is and isn’t allowed to do before you point it at anything.


Share this post:

Previous Post
Building AI Hackbots — 2026 External Pentest Edition
Next Post
AWP, Autonomous WebApp Pentester, and its first real engagement