Web Application Penetration Testing
A phased pentesting workflow for running web applications. Adapted from
Shannon's pipeline (Keygraph, AGPL — concepts only, no code borrowed).
Built around three rules:
- No exploit, no report — every finding requires reproducible evidence.
- Bounded scope — every active request goes against a target the operator
pre-declared. Off-scope hosts are refused.
- Bypass exhaustion before false-positive dismissal — a "blocked" payload
is not a clean bill of health until you've tried the bypass set.
⚠️ Hard Guardrails — Read Before Every Engagement
Violating any of these invalidates the engagement and may be illegal.
Authorization gate. Before the first active scan in a session, you
MUST confirm with the user, in writing, that they own or have written
authorization to test the target. Record the acknowledgement in
engagement/authorization.md (see template). No acknowledgement → no
active scanning. Reading public pages with curl is fine; sending
payloads is not.
Scope allowlist. Maintain engagement/scope.txt — one hostname or
CIDR per line. Every nmap, curl, whatweb, browser navigation, or
payload-bearing request MUST be against an entry in scope. If a target
redirects you off-scope (3xx to a different host, a link in HTML),
STOP and confirm with the user before following.
No production systems without paper. If the user hasn't told you
"yes, prod is in scope and I have written sign-off," assume not. Default
targets are staging, local docker, dedicated test instances.
Cloud metadata is off by default. Do not probe 169.254.169.254,
metadata.google.internal, 100.100.100.200, [fd00:ec2::254], or
equivalent unless the engagement explicitly includes SSRF-to-metadata
as a goal AND the target is one you control. The agent's browser tool
can reach these from inside your own infrastructure — don't.
Destructive payloads need approval. SQLi payloads that DROP/DELETE,
filesystem-write SSTI, command injection with rm/shutdown/mkfs,
anything that mutates beyond a single test row → ASK FIRST. The
approval.py system catches some; don't rely on it alone.
Aux-client leakage risk (Hermes-specific). This skill produces
sessions full of SQLi/XSS/RCE payloads, captured credentials, JWT
tokens. Hermes' compression and title-generation paths replay history
through the auxiliary client (often the main model). Anything sensitive
you write to the conversation can leave the box on the next compress.
Mitigation:
- Redact captured tokens/credentials to the LAST 6 CHARS before logging
them in any message. Full values go to
engagement/evidence/ files,
never into chat history.
- If the engagement is sensitive, set
auxiliary.title_generation.enabled: false
in ~/.hermes/config.yaml for the session.
Rate limit yourself. Default 200ms between active requests against
any single host. The recon-scan.sh script enforces this. Don't bypass
it without operator approval.
Authority of the report. This skill produces a security
assessment, not a "PASS." Even a clean run is "no exploitable issues
FOUND in scope X within time T using methods Y" — not "the application
is secure." Mirror that language in the report.
Phase 0: Engagement Setup
Before any scanning happens, create the engagement directory and
authorization acknowledgement.
ENGAGEMENT=engagement-$(date +%Y%m%d-%H%M%S)
mkdir -p "$ENGAGEMENT"/{evidence,findings,reports}
cd "$ENGAGEMENT"
Ask the user (verbatim):
"Confirm: (a) the target URL is [X], (b) you own this application
or have written authorization to test it, and (c) the engagement
may run for up to [N] hours starting now. Reply 'authorized' to
proceed."
Wait for explicit authorized response. Any other answer means STOP.
Record authorization to engagement/authorization.md using the
template in templates/authorization.md. Include:
- Target URL(s) and IP(s)
- Authorization basis (ownership / written authz from $name)
- Engagement window
- Out-of-scope items (production, third-party services, etc.)
- Operator name (the user driving this session)
Build scope.txt:
localhost
127.0.0.1
staging.example.com
192.168.1.0/24 # internal lab only, with operator OK
Read references/scope-enforcement.md before issuing the first
active request — that doc has the host-extraction rules you apply
to every command/URL before it goes out.
Phase 1: Pre-Recon (Code Analysis, optional)
Skip if no source access (black-box engagement).
If you have read access to the application source:
- Map the architecture — framework, routing, middleware stack
- Inventory sinks — every
execute(, os.system(, eval(,
template render, file read/write, redirect target
- Map auth — session cookie vs JWT, OAuth flows, password reset,
privileged endpoints
- Identify trust boundaries — what's authenticated, what's not,
what comes from
request.*
- Backward taint from each sink to a request source. Early-terminate
when proper sanitization is found (parameterized queries, allowlists,
shlex.quote, well-known escapers).
Output: evidence/pre-recon.md — architecture map, sink inventory,
suspected vulnerable code paths.
This is OFFLINE work. No traffic to the target.
Phase 2: Recon (Live, Read-Only)
Maps the attack surface. All requests are GETs of public pages, no
payloads yet. Still scope-bounded.
Verify scope. Resolve every target hostname → IP. Confirm IPs are
in scope (avoids the "DNS points somewhere unexpected" trap).
Network surface (only if scope permits port scanning):
nmap -sT -T3 --top-ports 100 -oN evidence/nmap.txt $TARGET
Use -T3 (default), not -T4/-T5. Stealthier and avoids tripping
IDS/IPS in shared environments.
Tech fingerprint:
whatweb -v $TARGET_URL > evidence/whatweb.txt
curl -sIk $TARGET_URL > evidence/headers.txt
Endpoint discovery:
- Crawl the app with the browser tool (
browser_navigate,
browser_get_images, follow links).
- Inspect
robots.txt, sitemap.xml, .well-known/*.
- Use the developer tools network panel via browser tool to capture
XHR/fetch calls.
Auth surface: Identify login, registration, password reset,
session cookie names, token formats. Do NOT send credentials yet —
just observe.
Correlate with pre-recon (if you have source). For each
evidence/pre-recon.md finding, mark whether the live surface
confirms it's reachable.
Output: evidence/recon.md — endpoints, technologies, auth model,
input vectors.
Phase 3: Vulnerability Analysis
One delegate_task per vulnerability class. Each agent reads
evidence/recon.md (+ evidence/pre-recon.md if present), produces
findings/<class>-queue.json using templates/exploitation-queue.json.
Use delegate_task with these focused subagents (parallel where possible):
| Class |
Goal |
Reference |
injection |
SQLi, command, path traversal, SSTI, LFI/RFI, deserialization |
references/vuln-taxonomy.md (slot types) |
xss |
Reflected, stored, DOM-based |
references/vuln-taxonomy.md (render contexts) |
auth |
Login bypass, JWT confusion, session fixation, OAuth flaws |
references/exploitation-techniques.md |
authz |
IDOR, vertical/horizontal escalation, business logic |
references/exploitation-techniques.md |
ssrf |
Internal reachability, metadata, protocol smuggling |
Skip metadata unless explicitly authorized |
infra |
Misconfig, info disclosure, default creds, exposed admin |
references/exploitation-techniques.md |
Each queue entry has: id, vuln class, source (file:line if known),
endpoint, parameter, slot type, suspected defense, verdict
(identified / partial / confirmed / critical), witness payload,
confidence (0-1), notes.
The analysis phase doesn't send malicious payloads yet — it stages them.
The exploitation phase actually fires them.
Phase 4: Exploitation (Proof-Based, Conditional)
Only run a sub-agent per class where the analysis queue has actionable
entries (identified or partial).
For each candidate:
- Pre-send check — host in scope? auth gate satisfied? payload
approved if destructive?
- Send the witness payload — minimal proof. SQLi:
' AND 1=1--
then ' AND 1=2--. XSS: a benign marker like
<svg/onload=console.log("HERMES-PENTEST-XSS")>. Never alert(1) in
stored XSS — it'll fire for other users in shared environments.
- Verify the witness fires — for blind injection, use a sleep
probe (
SLEEP(5)) and time the response. For SSRF, use a
tester-controlled callback host you own (NOT a public service like
webhook.site for sensitive engagements — exfil paths).
- Promote level:
- L1 Identified — pattern matched, no behavior change
- L2 Partial — sink reached, but defense in place
- L3 Confirmed — payload changed app behavior in observable way
- L4 Critical — data extracted, code executed, access escalated
- Bypass exhaustion before classifying as FP. For each candidate
that blocks: try at least the bypass set in
references/bypass-techniques.md for that class. Only after the set
is exhausted may you write verdict: false_positive.
- Record evidence for every L3/L4:
- Full request (method, URL, headers, body)
- Response (status, headers, relevant body excerpt)
- Reproducer command (curl one-liner)
- Impact statement
Output: findings/exploitation-evidence.md
Redact in evidence files:
- Any captured credentials/tokens → last 6 chars only in chat;
full value to
findings/secrets-vault.md (gitignored).
- Other users' PII → redact.
- Your test credentials → fine to keep.
Phase 5: Reporting
Generate the final report using templates/pentest-report.md. Sections:
- Executive summary
- Engagement scope (from
engagement/scope.txt)
- Authorization (from
engagement/authorization.md)
- Findings (L3/L4 only — proof-required). Per finding:
- Title, severity (CVSS 3.1), CWE
- Affected endpoint(s)
- Proof (request + response excerpt)
- Reproduction steps
- Impact
- Remediation
- Not-exploited candidates (L1/L2 with notes on what blocked them)
- Out-of-scope observations
- Methodology / tools used
- Limitations and what was NOT tested
Severity policy: CVSS only for L3/L4. L1/L2 are "candidates pending
verification" — don't assign CVSS to unverified findings.
When to Stop
- The user revokes authorization.
- A candidate finding clearly impacts production data and you don't have
approval for destructive testing — STOP and ask.
- The target starts returning 503/429 storms — back off, reconvene with
the operator.
- You discover something outside the contracted scope (e.g. an exposed
customer database while testing an unrelated endpoint). STOP, document,
report to the operator. Do not pivot without explicit approval — that
pivot is what makes pentesting illegal.
What This Skill Does NOT Cover
- Network-layer pentesting beyond port scanning (no Metasploit,
Cobalt Strike, AD attacks, network protocol fuzzing).
- Reverse engineering / binary analysis (see issue #383).
- Source-only static analysis (see issue #382).
- Active social engineering / phishing.
- Anything against systems the operator hasn't pre-authorized.
If the engagement needs any of these, escalate to a professional
pentester. This skill complements professional pentesting; it does
not replace it.
Further Reading
references/scope-enforcement.md — how to bound every active request
references/vuln-taxonomy.md — slot types, render contexts, OWASP map
references/exploitation-techniques.md — per-class payload patterns
references/bypass-techniques.md — common WAF/filter bypasses
templates/authorization.md — engagement authorization template
templates/pentest-report.md — final report template
templates/exploitation-queue.json — per-class finding queue schema
scripts/recon-scan.sh — rate-limited nmap+whatweb+headers wrapper
Source: NousResearch/hermes-agent → optional-skills/security/web-pentest/SKILL.md
1---2name: web-pentest3description: "| Authorized web application penetration testing — reconnaissance, vulnerability analysis, proof-based exploitation, and professional reporting. Adapts Shannon's \"No Exploit, No Report\" methodology with hard guardrails for scope, authorization, and aux-client leakage. Active testing against running applications you own or have written authorization to test."4category: security-and-compliance5source_repo: NousResearch/hermes-agent6source_path: "optional-skills/security/web-pentest/SKILL.md"7source_url: https://github.com/NousResearch/hermes-agent/blob/HEAD/optional-skills/security/web-pentest/SKILL.md8---91011# Web Application Penetration Testing1213A phased pentesting workflow for running web applications. Adapted from14Shannon's pipeline (Keygraph, AGPL — concepts only, no code borrowed).15Built around three rules:16171. No exploit, no report — every finding requires reproducible evidence.182. Bounded scope — every active request goes against a target the operator19 pre-declared. Off-scope hosts are refused.203. Bypass exhaustion before false-positive dismissal — a "blocked" payload21 is not a clean bill of health until you've tried the bypass set.2223---2425## ⚠️ Hard Guardrails — Read Before Every Engagement2627Violating any of these invalidates the engagement and may be illegal.28291. **Authorization gate.** Before the first active scan in a session, you30 MUST confirm with the user, in writing, that they own or have written31 authorization to test the target. Record the acknowledgement in32 `engagement/authorization.md` (see template). No acknowledgement → no33 active scanning. Reading public pages with `curl` is fine; sending34 payloads is not.35362. **Scope allowlist.** Maintain `engagement/scope.txt` — one hostname or37 CIDR per line. Every `nmap`, `curl`, `whatweb`, browser navigation, or38 payload-bearing request MUST be against an entry in scope. If a target39 redirects you off-scope (3xx to a different host, a link in HTML),40 STOP and confirm with the user before following.41423. **No production systems without paper.** If the user hasn't told you43 "yes, prod is in scope and I have written sign-off," assume not. Default44 targets are staging, local docker, dedicated test instances.45464. **Cloud metadata is off by default.** Do not probe `169.254.169.254`,47 `metadata.google.internal`, `100.100.100.200`, `[fd00:ec2::254]`, or48 equivalent unless the engagement explicitly includes SSRF-to-metadata49 as a goal AND the target is one you control. The agent's browser tool50 can reach these from inside your own infrastructure — don't.51525. **Destructive payloads need approval.** SQLi payloads that DROP/DELETE,53 filesystem-write SSTI, command injection with `rm`/`shutdown`/`mkfs`,54 anything that mutates beyond a single test row → ASK FIRST. The55 `approval.py` system catches some; don't rely on it alone.56576. **Aux-client leakage risk (Hermes-specific).** This skill produces58 sessions full of SQLi/XSS/RCE payloads, captured credentials, JWT59 tokens. Hermes' compression and title-generation paths replay history60 through the auxiliary client (often the main model). Anything sensitive61 you write to the conversation can leave the box on the next compress.62 Mitigation:63 - Redact captured tokens/credentials to the LAST 6 CHARS before logging64 them in any message. Full values go to `engagement/evidence/` files,65 never into chat history.66 - If the engagement is sensitive, set `auxiliary.title_generation.enabled: false`67 in `~/.hermes/config.yaml` for the session.68697. **Rate limit yourself.** Default 200ms between active requests against70 any single host. The recon-scan.sh script enforces this. Don't bypass71 it without operator approval.72738. **Authority of the report.** This skill produces a security74 assessment, not a "PASS." Even a clean run is "no exploitable issues75 FOUND in scope X within time T using methods Y" — not "the application76 is secure." Mirror that language in the report.7778---7980## Phase 0: Engagement Setup8182Before any scanning happens, create the engagement directory and83authorization acknowledgement.8485```bash86ENGAGEMENT=engagement-$(date +%Y%m%d-%H%M%S)87mkdir -p "$ENGAGEMENT"/{evidence,findings,reports}88cd "$ENGAGEMENT"89```90911. **Ask the user (verbatim):**92 > "Confirm: (a) the target URL is [X], (b) you own this application93 > or have written authorization to test it, and (c) the engagement94 > may run for up to [N] hours starting now. Reply 'authorized' to95 > proceed."96972. **Wait for explicit `authorized` response.** Any other answer means STOP.98993. **Record authorization** to `engagement/authorization.md` using the100 template in `templates/authorization.md`. Include:101 - Target URL(s) and IP(s)102 - Authorization basis (ownership / written authz from $name)103 - Engagement window104 - Out-of-scope items (production, third-party services, etc.)105 - Operator name (the user driving this session)1061074. **Build scope.txt:**108 ```109 localhost110 127.0.0.1111 staging.example.com112 192.168.1.0/24 # internal lab only, with operator OK113 ```1141155. **Read** `references/scope-enforcement.md` before issuing the first116 active request — that doc has the host-extraction rules you apply117 to every command/URL before it goes out.118119---120121## Phase 1: Pre-Recon (Code Analysis, optional)122123Skip if no source access (black-box engagement).124125If you have read access to the application source:1261271. **Map the architecture** — framework, routing, middleware stack1282. **Inventory sinks** — every `execute(`, `os.system(`, `eval(`,129 template render, file read/write, redirect target1303. **Map auth** — session cookie vs JWT, OAuth flows, password reset,131 privileged endpoints1324. **Identify trust boundaries** — what's authenticated, what's not,133 what comes from `request.*`1345. **Backward taint** from each sink to a request source. Early-terminate135 when proper sanitization is found (parameterized queries, allowlists,136 `shlex.quote`, well-known escapers).137138Output: `evidence/pre-recon.md` — architecture map, sink inventory,139suspected vulnerable code paths.140141This is OFFLINE work. No traffic to the target.142143---144145## Phase 2: Recon (Live, Read-Only)146147Maps the attack surface. All requests are GETs of public pages, no148payloads yet. Still scope-bounded.1491501. **Verify scope.** Resolve every target hostname → IP. Confirm IPs are151 in scope (avoids the "DNS points somewhere unexpected" trap).1521532. **Network surface** (only if scope permits port scanning):154 ```bash155 nmap -sT -T3 --top-ports 100 -oN evidence/nmap.txt $TARGET156 ```157 Use `-T3` (default), not `-T4/-T5`. Stealthier and avoids tripping158 IDS/IPS in shared environments.1591603. **Tech fingerprint:**161 ```bash162 whatweb -v $TARGET_URL > evidence/whatweb.txt163 curl -sIk $TARGET_URL > evidence/headers.txt164 ```1651664. **Endpoint discovery:**167 - Crawl the app with the browser tool (`browser_navigate`,168 `browser_get_images`, follow links).169 - Inspect `robots.txt`, `sitemap.xml`, `.well-known/*`.170 - Use the developer tools network panel via browser tool to capture171 XHR/fetch calls.1721735. **Auth surface:** Identify login, registration, password reset,174 session cookie names, token formats. Do NOT send credentials yet —175 just observe.1761776. **Correlate with pre-recon** (if you have source). For each178 `evidence/pre-recon.md` finding, mark whether the live surface179 confirms it's reachable.180181Output: `evidence/recon.md` — endpoints, technologies, auth model,182input vectors.183184---185186## Phase 3: Vulnerability Analysis187188One delegate_task per vulnerability class. Each agent reads189`evidence/recon.md` (+ `evidence/pre-recon.md` if present), produces190`findings/<class>-queue.json` using `templates/exploitation-queue.json`.191192Use `delegate_task` with these focused subagents (parallel where possible):193194| Class | Goal | Reference |195|-------|------|-----------|196| `injection` | SQLi, command, path traversal, SSTI, LFI/RFI, deserialization | `references/vuln-taxonomy.md` (slot types) |197| `xss` | Reflected, stored, DOM-based | `references/vuln-taxonomy.md` (render contexts) |198| `auth` | Login bypass, JWT confusion, session fixation, OAuth flaws | `references/exploitation-techniques.md` |199| `authz` | IDOR, vertical/horizontal escalation, business logic | `references/exploitation-techniques.md` |200| `ssrf` | Internal reachability, metadata, protocol smuggling | Skip metadata unless explicitly authorized |201| `infra` | Misconfig, info disclosure, default creds, exposed admin | `references/exploitation-techniques.md` |202203Each queue entry has: id, vuln class, source (file:line if known),204endpoint, parameter, slot type, suspected defense, verdict205(`identified` / `partial` / `confirmed` / `critical`), witness payload,206confidence (0-1), notes.207208The analysis phase doesn't send malicious payloads yet — it stages them.209The exploitation phase actually fires them.210211---212213## Phase 4: Exploitation (Proof-Based, Conditional)214215Only run a sub-agent per class where the analysis queue has actionable216entries (`identified` or `partial`).217218For each candidate:2192201. **Pre-send check** — host in scope? auth gate satisfied? payload221 approved if destructive?2222. **Send the witness payload** — minimal proof. SQLi: `' AND 1=1--`223 then `' AND 1=2--`. XSS: a benign marker like224 `<svg/onload=console.log("HERMES-PENTEST-XSS")>`. Never `alert(1)` in225 stored XSS — it'll fire for other users in shared environments.2263. **Verify the witness fires** — for blind injection, use a sleep227 probe (`SLEEP(5)`) and time the response. For SSRF, use a228 tester-controlled callback host you own (NOT a public service like229 webhook.site for sensitive engagements — exfil paths).2304. **Promote level:**231 - **L1 Identified** — pattern matched, no behavior change232 - **L2 Partial** — sink reached, but defense in place233 - **L3 Confirmed** — payload changed app behavior in observable way234 - **L4 Critical** — data extracted, code executed, access escalated2355. **Bypass exhaustion before classifying as FP.** For each candidate236 that blocks: try at least the bypass set in237 `references/bypass-techniques.md` for that class. Only after the set238 is exhausted may you write `verdict: false_positive`.2396. **Record evidence** for every L3/L4:240 - Full request (method, URL, headers, body)241 - Response (status, headers, relevant body excerpt)242 - Reproducer command (curl one-liner)243 - Impact statement244245Output: `findings/exploitation-evidence.md`246247**Redact in evidence files:**248- Any captured credentials/tokens → last 6 chars only in chat;249 full value to `findings/secrets-vault.md` (gitignored).250- Other users' PII → redact.251- Your test credentials → fine to keep.252253---254255## Phase 5: Reporting256257Generate the final report using `templates/pentest-report.md`. Sections:2582591. Executive summary2602. Engagement scope (from `engagement/scope.txt`)2613. Authorization (from `engagement/authorization.md`)2624. Findings (L3/L4 only — proof-required). Per finding:263 - Title, severity (CVSS 3.1), CWE264 - Affected endpoint(s)265 - Proof (request + response excerpt)266 - Reproduction steps267 - Impact268 - Remediation2695. Not-exploited candidates (L1/L2 with notes on what blocked them)2706. Out-of-scope observations2717. Methodology / tools used2728. Limitations and what was NOT tested273274**Severity policy:** CVSS only for L3/L4. L1/L2 are "candidates pending275verification" — don't assign CVSS to unverified findings.276277---278279## When to Stop280281- The user revokes authorization.282- A candidate finding clearly impacts production data and you don't have283 approval for destructive testing — STOP and ask.284- The target starts returning 503/429 storms — back off, reconvene with285 the operator.286- You discover something *outside* the contracted scope (e.g. an exposed287 customer database while testing an unrelated endpoint). STOP, document,288 report to the operator. Do not pivot without explicit approval — that289 pivot is what makes pentesting illegal.290291---292293## What This Skill Does NOT Cover294295- Network-layer pentesting beyond port scanning (no Metasploit,296 Cobalt Strike, AD attacks, network protocol fuzzing).297- Reverse engineering / binary analysis (see issue #383).298- Source-only static analysis (see issue #382).299- Active social engineering / phishing.300- Anything against systems the operator hasn't pre-authorized.301302If the engagement needs any of these, escalate to a professional303pentester. This skill complements professional pentesting; it does304not replace it.305306---307308## Further Reading309310- `references/scope-enforcement.md` — how to bound every active request311- `references/vuln-taxonomy.md` — slot types, render contexts, OWASP map312- `references/exploitation-techniques.md` — per-class payload patterns313- `references/bypass-techniques.md` — common WAF/filter bypasses314- `templates/authorization.md` — engagement authorization template315- `templates/pentest-report.md` — final report template316- `templates/exploitation-queue.json` — per-class finding queue schema317- `scripts/recon-scan.sh` — rate-limited nmap+whatweb+headers wrapper318319---320321**Source:** [`NousResearch/hermes-agent`](https://github.com/NousResearch/hermes-agent) → `optional-skills/security/web-pentest/SKILL.md`