Pentest Script Generator — SOP (Strix-native v2)
Generate two professional Python scripts for each vuln-XXXX.md report.
Updated from analysis of 196 real-world vulnerabilities.
Language rule: All skill instructions and generated script code use English.
Final summary presented to the user must be in Vietnamese.
Step 1 — Parse vuln-XXXX.md (Strix schema)
Schema produced by tracer.py → create_vulnerability_report():
# <title>
**ID:** vuln-XXXX
**Severity:** CRITICAL | HIGH | MEDIUM | LOW
**Found:** <ISO timestamp UTC>
**Target:** <base URL>
**Endpoint:** <path> ← optional
**Method:** <HTTP method> ← optional
**CVE:** CVE-YYYY-NNNNN ← optional
**CWE:** CWE-NNN ← optional
**CVSS:** <float>
## Description
## Impact
## Technical Analysis
## Proof of Concept
<poc_description>
```<poc_script_code — ALWAYS PRESENT>```
## Code Analysis ← optional, whitebox
## Remediation
Key rule: poc_script_code is always present — use it as the primary source for test logic.
If Endpoint is missing → extract from Technical Analysis or PoC code.
Step 2 — Identify vulnerability type
Authorization / Access Control
| Type | Trigger Keywords | TC Pattern |
|---|
| BFLA | admin endpoint, non-admin users can access, BFLA | Low-priv token → admin route |
| IDOR/BOLA | cross-user, order id, user_id param | Swap resource ID to another user |
| Unauth API | unauthenticated access, no auth required | Request with no Authorization header |
| Supabase PostgREST | anon apikey, PostgREST, RLS bypass | Call REST with public apikey |
| Firestore Rules | Firestore, RLS misconfiguration | Read/write with anonymous user |
| Unauth Order Ops | order cancellation, order status | GET/POST order endpoint without token |
Business Logic
| Type | Trigger Keywords | TC Pattern |
|---|
| Price Tampering | price tampering, client-side price | POST order with price=1 |
| Payment Bypass | payment bypass, free license, paid without | Create order, skip payment step |
| Negative Total | negative totals, wallet credit | Send negative amount |
| Rounding Error | rounding error, free credit | Try decimal edge-case values |
| Rate Limit Bypass | X-Forwarded-For, IP spoofing | Add X-Forwarded-For: 1.2.3.X header |
| OTP Brute Force | missing OTP brute-force protection | Send many wrong OTPs in sequence |
| Registration Farming | unlimited trial, credit farming | Create multiple accounts for trial |
Injection / XSS
| Type | Trigger Keywords | TC Pattern |
|---|
| Stored XSS | stored xss, innerHTML, marked.parse | Upload payload with unique marker |
| DOM XSS | DOM-based, innerHTML, url param | Playwright + javascript: URL |
| Reflected XSS | reflected xss, POST body reflection | GET/POST with payload in param |
| JSONP Injection | JSONP callback, arbitrary JS | Call callback with random function name |
| Script Injection | installer script, key parameter | Send bash command substitution payload |
| SQLi | SQL injection, remember_token cookie | SQLi payload via cookie/param |
SSRF
| Type | Trigger Keywords | TC Pattern |
|---|
| Blind SSRF | blind SSRF, OAST, webhook | Send webhook.site domain |
| SSRF IMDS | cloud metadata, IMDS, 169.254 | Send http://169.254.169.254/ |
| SSRF Internal | internal network, port scanning | Send 127.0.0.1.nip.io variants |
| SSRF xmlrpc | wordpress xmlrpc, pingback | POST xmlrpc.php with internal URL |
Account Takeover
| Type | Trigger Keywords | TC Pattern |
|---|
| Password Reset Leak | plaintext password, password in response | POST forgot-password → check response for password field |
| Account Enumeration | differential error, user enumeration | Try existing vs non-existing email, compare response |
| Session Fixation | PHPSESSID not rotated, session fixation | Capture session before login, verify PHPSESSID changes after |
| Token Not Revoked | token remains valid after logout | Logout → replay old token on API |
| OTP Disclosed | OTP in response, OTP disclosed | POST forgot → check response for OTP field |
| Default Credentials | default credentials, admin:admin | Login with default credential pairs |
CORS / Cookie
| Type | Trigger Keywords | TC Pattern |
|---|
| CORS Arbitrary Origin | arbitrary origin reflection, CORS misconfiguration | Send Origin: https://evil.com, check Access-Control-Allow-Origin |
| Cookie Missing Secure | missing Secure flag, session cookie HTTP | Send HTTP request, check Set-Cookie flags |
| CSRF | CSRF, state-changing GET | Send request without CSRF token |
Information Disclosure
| Type | Trigger Keywords | TC Pattern |
|---|
| Source Map | sourcemap, sourcesContent | GET .js.map file, check sourcesContent |
| Hardcoded Key in JS | hardcoded API key, JS bundle | Fetch JS file, grep for key patterns |
| Laravel Debug | Laravel debug, stack trace, Ignition | GET endpoint with Accept: text/html |
| Vite Dev Server | Vite dev server, /@fs | GET /@vite/client, /@fs/etc/passwd |
| Backup Archive | backup archive, .zip, .sql dump | GET /htdocs.zip, /backup.sql |
| phpinfo Exposure | phpinfo, /info.php | GET /info.php |
Infrastructure
| Type | Trigger Keywords | TC Pattern |
|---|
| Cloudflare Origin Bypass | origin exposure, WAF bypass | Resolve subdomain, call origin IP directly |
| Exposed DB | exposed MySQL, MariaDB, Redis public | Check open ports, read banner |
| Kubernetes Exposed | Kubernetes API, TCP/6443 | GET /version, /healthz |
| WordPress User Enum | WordPress REST API, user enumeration | GET /wp-json/wp/v2/users |
Step 3 — Identify auth mechanism
| PoC Signal | Auth Type | How to obtain |
|---|
Authorization: Bearer eyJ... | JWT | register → login → access_token |
apikey= or x-api-key | API Key | From config/profile endpoint |
Supabase anon key | Supabase anon | From JS bundle or network tab |
Cookie PHPSESSID / session | PHP session | Login form → capture cookie |
recaptchaToken: "" | reCAPTCHA bypass | Send empty string |
| No token in PoC | Unauthenticated | No Authorization header needed |
X-Forwarded-For | Rate limit bypass | Add spoofed IP header |
Step 4 — Standard code template (use for all pentest scripts)
#!/usr/bin/env python3
"""
============================================================================
Penetration Test Script — vuln-XXXX — <title>
Target : <target>
Severity : <SEVERITY> | CVSS: <score>
CWE : <CWE-NNN>
============================================================================
Description: <1-2 sentences>
Test Cases:
TC-01: ...
TC-0N: Cleanup
Usage:
pip install requests colorama
python pentest_TYPE_vulnXXXX.py [--base-url URL] [--dry-run] [--output-dir DIR]
⚠️ RUN ONLY ON AUTHORIZED TEST TARGETS ⚠️
============================================================================
"""
import argparse, json, os, time, sys
from dataclasses import dataclass, field
from datetime import datetime, timezone
from enum import Enum
from typing import Any, Optional
try:
import requests; from requests.exceptions import RequestException
except ImportError:
print("[!] pip install requests"); sys.exit(1)
try:
from colorama import Fore, Style, init as colorama_init; colorama_init(autoreset=True)
except ImportError:
class _Stub:
def __getattr__(self, _): return ""
Fore = Style = _Stub()
class Verdict(Enum):
VULN="VULNERABLE"; SAFE="SAFE"; ERROR="ERROR"; INFO="INFO"; SKIP="SKIPPED"
@dataclass
class TestResult:
test_id: str; name: str; verdict: Verdict
status_code: Optional[int]=None; detail: str=""
response_preview: str=""; raw_response: Any=None
timestamp: str=field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
def _print_result(r: TestResult):
c={Verdict.VULN:Fore.RED,Verdict.SAFE:Fore.GREEN,Verdict.ERROR:Fore.YELLOW,
Verdict.INFO:Fore.BLUE,Verdict.SKIP:Fore.WHITE}.get(r.verdict,"")
i={Verdict.VULN:"✗ VULNERABLE",Verdict.SAFE:"✓ SAFE",Verdict.ERROR:"⚠ ERROR",
Verdict.INFO:"ℹ INFO",Verdict.SKIP:"⊘ SKIPPED"}.get(r.verdict,"?")
print(f"\n{c}[{r.test_id}] {r.name}\n Result : {i}")
if r.status_code: print(f" HTTP : {r.status_code}")
if r.detail: print(f" Detail : {r.detail}")
if r.response_preview: print(f" Response: {r.response_preview[:300]}")
def _summary(results, vuln_id):
v=sum(1 for r in results if r.verdict==Verdict.VULN)
s=sum(1 for r in results if r.verdict==Verdict.SAFE)
e=sum(1 for r in results if r.verdict==Verdict.ERROR)
print(f"\n{'═'*68}\n SUMMARY — {vuln_id}")
for r in results:
c=Fore.RED if r.verdict==Verdict.VULN else Fore.GREEN if r.verdict==Verdict.SAFE \
else Fore.YELLOW if r.verdict==Verdict.ERROR else Fore.BLUE
print(f" {c}[{r.verdict.value:12s}] {r.test_id}: {r.name}")
print(f"\n Vulnerable: {v} | Safe: {s} | Error: {e}")
if v: print(f"\n {Fore.RED}⚠ CONCLUSION: VULNERABILITY CONFIRMED ({v} endpoint(s))")
def _export(results, vuln_id, target, output_dir):
os.makedirs(output_dir, exist_ok=True)
p = os.path.join(output_dir, f"pentest_{vuln_id}_report.json")
with open(p,"w",encoding="utf-8") as f:
json.dump({"vulnerability_id":vuln_id,"target":target,
"timestamp":datetime.now(timezone.utc).isoformat(),
"summary":{"total":len(results),
"vulnerable":sum(1 for r in results if r.verdict==Verdict.VULN),
"safe":sum(1 for r in results if r.verdict==Verdict.SAFE)},
"results":[{"test_id":r.test_id,"name":r.name,"verdict":r.verdict.value,
"status_code":r.status_code,"detail":r.detail,
"raw_response":r.raw_response,"timestamp":r.timestamp}
for r in results]},f,indent=2,ensure_ascii=False,default=str)
print(f"\n {Fore.CYAN}📄 Report: {p}")
Step 5 — TC patterns by vulnerability type
BFLA / IDOR
TC-01: Register test account (recaptchaToken="" if bypass available)
TC-02: Login → capture JWT access_token
TC-03: Decode JWT → confirm role = "user"
TC-04: Call admin endpoint with user token → expect 403; 200 = VULNERABLE
TC-05: Control: call properly-enforced endpoint → confirm 403
TC-06: (IDOR) Swap resource ID to another user's resource
TC-07: Cleanup
Unauthenticated API / Supabase
TC-01: GET endpoint with NO Authorization header → expect 401/403; 200 = VULNERABLE
TC-02: GET with valid token → expect 200 (confirm endpoint functions)
TC-03: GET with invalid token → expect 401
# Supabase variant:
TC-01: GET /rest/v1/{table} with header "apikey: <anon_key>" → inspect returned data
TC-02: No apikey header → expect 401
Business Logic — Price Tampering
TC-01: Create order with original price → expect success (baseline)
TC-02: Create order with price=1 (tampered) → if success = VULNERABLE
TC-03: Create order with price=-1 (negative) → observe response
TC-04: Cleanup — cancel created orders
Business Logic — Payment Bypass
TC-01: Check if confirm-payment endpoint requires valid payment token
TC-02: POST confirm with arbitrary order_id → expect 403/404; 200 = VULNERABLE
TC-03: Check order status after confirm
TC-04: Cleanup
Account Takeover — Password Reset Leak
TC-01: POST /forgot-password with victim email
TC-02: Check response body for "password" field → VULNERABLE if present
TC-03: Attempt login with password from response → confirms full ATO
TC-04: (Optional) Try non-existent email → compare response (enumeration check)
Account Takeover — Token Not Revoked After Logout
TC-01: Login → capture access_token
TC-02: Call authenticated API → expect 200 (token valid)
TC-03: POST /logout (or /api/auth/logout)
TC-04: Replay old token on authenticated API → expect 401; 200 = VULNERABLE
Rate Limit Bypass via X-Forwarded-For
TC-01: Send N requests with real IP → record when blocked
TC-02: Send N+1 requests with X-Forwarded-For: <random_IP> → if not blocked = VULNERABLE
TC-03: Test X-Real-IP, CF-Connecting-IP header variants
CORS Arbitrary Origin
TC-01: GET endpoint with Origin: https://evil.com
→ check Access-Control-Allow-Origin: https://evil.com = VULNERABLE
TC-02: GET with Origin: null → check response
TC-03: GET with no Origin → baseline
TC-04: If credentials: check Access-Control-Allow-Credentials: true (escalates severity)
Stored XSS (Upload)
TC-01: Get auth token (if required)
TC-02: Upload SVG/HTML file with payload + unique marker
TC-03: Fetch CDN URL → check Content-Type and payload presence
TC-04: (Playwright) Browser-verify JS execution via dialog event
TC-05: Cleanup — DELETE uploaded file
SSRF (Blind)
TC-01: Get auth token
TC-02: Trigger with OAST domain (webhook.site) → print manual check instructions
TC-03: Trigger with 127.0.0.1.nip.io, 10.0.0.1.nip.io
TC-04: (if applicable) Trigger with http://169.254.169.254/latest/meta-data/
TC-05: Print manual checklist
TC-06: Cleanup
Information Disclosure — Source Map
TC-01: GET /main-XXXX.js → check for "//# sourceMappingURL=" comment
TC-02: GET /main-XXXX.js.map → check for "sourcesContent" field
TC-03: Analyze sourcesContent for hardcoded secrets (key, password, apikey)
Information Disclosure — Laravel Debug / Vite Dev Server
TC-01: GET endpoint with Accept: text/html → check for Ignition/debug page
TC-02: GET /@vite/client → 200 = Vite dev server exposed
TC-03: GET /@fs/etc/passwd → check for file read
TC-04: GET /info.php → check for phpinfo() output
WordPress
TC-01: GET /wp-json/wp/v2/users?per_page=100 → user enumeration
TC-02: GET /?author=1 → redirect-based user enumeration
TC-03: POST /xmlrpc.php with system.listMethods payload
TC-04: POST /xmlrpc.php with pingback SSRF payload → OAST domain
Cloudflare Origin Bypass
TC-01: Resolve subdomain (e.g., api.example.com) → obtain origin IP
TC-02: GET http://<origin_IP>/ directly → compare with CDN response
TC-03: Check WAF/rate-limit bypass on direct origin access
Session Fixation
TC-01: GET login page → capture PHPSESSID from Set-Cookie
TC-02: POST login with credentials + same pre-login session cookie
TC-03: Check PHPSESSID after login → UNCHANGED = VULNERABLE
Step 6 — Generate verify_vulnXXXX.py
Minimal script: 1–3 requests, exit 0/1/2:
#!/usr/bin/env python3
"""
Verify Script — vuln-XXXX — <title>
Exit codes: 0=SAFE 1=VULNERABLE 2=ERROR
Usage: python verify_vulnXXXX.py [--base-url URL]
"""
import sys, argparse, requests
requests.packages.urllib3.disable_warnings()
DEFAULT = "<TARGET>"
def verify(base_url: str) -> int:
try:
r = requests.get(f"{base_url}/endpoint", timeout=15, verify=False)
if r.status_code == 200:
print(f"[VULNERABLE] {r.status_code} — <reason>")
print(f" {r.text[:300]}")
return 1
elif r.status_code in (401, 403):
print(f"[SAFE] {r.status_code} — endpoint properly protected")
return 0
print(f"[UNKNOWN] {r.status_code}")
return 2
except Exception as e:
print(f"[ERROR] {e}"); return 2
if __name__ == "__main__":
p = argparse.ArgumentParser()
p.add_argument("--base-url", default=DEFAULT)
sys.exit(verify(p.parse_args().base_url))
Step 7 — File naming convention
| Vulnerability Type | Type Prefix |
|---|
| BFLA | bfla |
| IDOR / BOLA | idor |
| Unauthenticated API | unauth |
| Stored XSS | xss_stored |
| DOM XSS | xss_dom |
| Reflected XSS | xss_reflected |
| SSRF | ssrf |
| Business Logic (price/payment) | bizlogic |
| Account Takeover | ato |
| CORS | cors |
| SQL Injection | sqli |
| Information Disclosure | infodisclosure |
| Rate Limit | ratelimit |
| Session/Cookie | session |
| WordPress-specific | wordpress |
| Supabase-specific | supabase |
| Infrastructure | infra |
Save output to: <project_dir>/vulnerabilities/
Step 8 — Workflow
1. READ → Parse vuln-XXXX.md (or description / raw HTTP request)
2. DETECT → Identify vuln type from trigger keywords
3. AUTH → Identify auth mechanism from PoC signals
4. WRITE → Generate pentest_TYPE_vulnXXXX.py
- Header with metadata (ID, Severity, CVSS, CWE, Target)
- Verdict enum + TestResult dataclass
- TC-01..TC-N methods (exploit + control + cleanup)
- _save_evidence() helper
- _summary() and _export() helpers
- main() with argparse (--base-url, --dry-run, --output-dir)
5. WRITE → Generate verify_vulnXXXX.py (minimal, exit 0/1/2)
6. REPORT → Present final summary to user IN VIETNAMESE (see Step 9)
Step 9 — Final report to user (ALWAYS in Vietnamese)
After generating both files, present this summary in Vietnamese:
✅ Đã tạo xong cho vuln-XXXX (<tiêu đề>):
📄 vulnerabilities/vuln-XXXX.md (Severity: X, CVSS: Y)
🐍 vulnerabilities/pentest_TYPE_vulnXXXX.py (N test cases)
⚡ vulnerabilities/verify_vulnXXXX.py (quick verify, exit 0/1/2)
▶ Cách chạy:
python vulnerabilities/pentest_TYPE_vulnXXXX.py --base-url https://target.com
python vulnerabilities/verify_vulnXXXX.py --base-url https://target.com
📋 Tóm tắt:
- Loại lỗ hổng : <TYPE>
- Auth mechanism: <auth type>
- Số test cases : N
- Cleanup : ✅ included
Anti-Patterns
| ❌ Don't | ✅ Do |
|---|
| Hardcode credentials in script | Use argparse + env vars |
| Skip cleanup TC | Always add cleanup as last TC |
| Claim vuln without HTTP evidence | Log status_code + response preview |
Use requests.get without timeout | Always timeout=15 |
| Skip dry-run mode | Always support --dry-run flag |
| Generate script without verify script | Always generate both |
| Present results to user in English | Always present final summary in Vietnamese |