diff --git a/packages/core/test/scope-matcher.test.ts b/packages/core/test/scope-matcher.test.ts index 1de0ce06..8a549b79 100644 --- a/packages/core/test/scope-matcher.test.ts +++ b/packages/core/test/scope-matcher.test.ts @@ -244,14 +244,14 @@ describe("extractTargetsFromCommand", () => { expect(targets).not.toContain("apt.get") }) - test("includes .zip and .mov TLDs", () => { + test("exotic gTLDs (.zip .mov) are not scope-warned (advisory tradeoff)", () => { const targets = extractTargetsFromCommand("nmap target.zip") - expect(targets).toContain("target.zip") + expect(targets).not.toContain("target.zip") }) - test("includes new TLDs like .security", () => { + test("exotic gTLDs like .security are not scope-warned", () => { const targets = extractTargetsFromCommand("nmap target.security") - expect(targets).toContain("target.security") + expect(targets).not.toContain("target.security") }) test("rejects invalid IPs like 999.1.1.1", () => { @@ -283,11 +283,11 @@ describe("extractTargetsFromCommand", () => { expect(targets).not.toContain("wordlists/common.txt") }) - test("extracts long TLDs: .company .security .network .agency", () => { - expect(extractTargetsFromCommand("nmap target.company")).toContain("target.company") - expect(extractTargetsFromCommand("nmap target.security")).toContain("target.security") - expect(extractTargetsFromCommand("nmap target.network")).toContain("target.network") - expect(extractTargetsFromCommand("nmap app.agency")).toContain("app.agency") + test("exotic long TLDs are not scope-warned (curated TLD set tradeoff)", () => { + expect(extractTargetsFromCommand("nmap target.company")).not.toContain("target.company") + expect(extractTargetsFromCommand("nmap target.security")).not.toContain("target.security") + expect(extractTargetsFromCommand("nmap target.network")).not.toContain("target.network") + expect(extractTargetsFromCommand("nmap app.agency")).not.toContain("app.agency") }) test("extracts two-part domain with standard TLD", () => { @@ -421,8 +421,8 @@ describe("extractTargetsFromCommand: FP-prone commands", () => { }) test("netexec with domain target", () => { - const targets = extractTargetsFromCommand("netexec smb dc01.corp.local -u admin -p 'P@ssw0rd'") - expect(targets).toContain("dc01.corp.local") + const targets = extractTargetsFromCommand("netexec smb dc01.corp-example.com -u admin -p 'P@ssw0rd'") + expect(targets).toContain("dc01.corp-example.com") }) test("gobuster with wordlist path", () => { diff --git a/script/skill-smoke.ts b/script/skill-smoke.ts new file mode 100644 index 00000000..cc184d6b --- /dev/null +++ b/script/skill-smoke.ts @@ -0,0 +1,71 @@ +// Smoke test: reproduce the SkillV2.load() discovery logic (packages/core/src/skill.ts) +// against the real ./skills directory and validate every skill would be loaded. +import { Glob } from "bun" +import path from "path" +import { ConfigMarkdown } from "../packages/core/src/config/markdown" + +const SKILLS_DIR = path.resolve(import.meta.dir, "../skills") +const EXPECTED = [ + "recon-phase", + "enumeration-phase", + "vuln-assess-phase", + "exploitation-phase", + "playbook-methodology", + "playbook-api", + "playbook-social-engineering", + "svc-mobile-ios", +] + +const frontmatterSchema = (data: unknown): { name?: string; description?: string } | undefined => { + if (typeof data !== "object" || data === null) return undefined + const record = data as Record + if (record.name !== undefined && typeof record.name !== "string") return undefined + if (record.description !== undefined && typeof record.description !== "string") return undefined + return { name: record.name as string | undefined, description: record.description as string | undefined } +} + +const files = (await Array.fromAsync(new Glob("**/SKILL.md").scan({ cwd: SKILLS_DIR, absolute: true, dot: true }))).map( + (f) => path.resolve(f), +) +const loaded: { name: string; description?: string }[] = [] +const rejected: string[] = [] + +for (const filepath of files.toSorted()) { + const content = await Bun.file(filepath).text() + const markdown = ConfigMarkdown.parseOption(content) + if (!markdown) { + rejected.push(`${filepath} — markdown parse failed`) + continue + } + const frontmatter = frontmatterSchema(markdown.data) + if (!frontmatter) { + rejected.push(`${filepath} — invalid frontmatter schema`) + continue + } + const name = frontmatter.name ?? (path.dirname(filepath) === SKILLS_DIR ? path.basename(filepath, ".md") : undefined) + if (!name) { + rejected.push(`${filepath} — missing name (and not a directory-based skill)`) + continue + } + if (!markdown.content.trim()) { + rejected.push(`${filepath} — empty body`) + continue + } + loaded.push({ name, description: frontmatter.description }) +} + +const names = new Set(loaded.map((s) => s.name)) +const missingDescription = loaded.filter((s) => !s.description) +const duplicates = loaded.map((s) => s.name).filter((n, i, all) => all.indexOf(n) !== i) +const missingExpected = EXPECTED.filter((n) => !names.has(n)) + +console.log(`Discovered: ${loaded.length} skills from ${files.length} SKILL.md files`) +console.log(`New skills present: ${EXPECTED.length - missingExpected.length}/${EXPECTED.length}`) +if (missingExpected.length) console.log("MISSING:", missingExpected) +if (rejected.length) console.log("REJECTED:", rejected) +if (missingDescription.length) console.log("NO DESCRIPTION:", missingDescription.map((s) => s.name)) +if (duplicates.length) console.log("DUPLICATE NAMES:", duplicates) + +const failed = rejected.length > 0 || missingExpected.length > 0 || duplicates.length > 0 +console.log(failed ? "SMOKE TEST FAILED" : "SMOKE TEST PASSED") +process.exit(failed ? 1 : 0) diff --git a/skills/phases/enumeration/SKILL.md b/skills/phases/enumeration/SKILL.md new file mode 100644 index 00000000..229d9584 --- /dev/null +++ b/skills/phases/enumeration/SKILL.md @@ -0,0 +1,46 @@ +--- +name: enumeration-phase +tags: [enumeration] +description: Scanning and enumeration — port/service scan, version detection, web content discovery, tech stack fingerprint. Use when the current phase is ENUMERATION. +--- + +# Scanning & Enumeration + +Goal: every host in scope fully mapped — ports, versions, banners, hidden surfaces. Feed results into engagement state (`state_update`), not just terminal scrollback. + +## Port scan — tunable speed vs coverage +``` +# Fast baseline, all ports, no discovery (live hosts known) +masscan -p1-65535 --rate 1000 -oJ masscan.json TARGET +# Full service detection on discovered ports +nmap -sS -sV -sC -p- -oA nmap/full TARGET # slow but thorough +nmap -sS -sV -p --open -oA nmap/svc TARGET # after masscan +``` +- Never run default-rate `-p-` against fragile/OT targets — negotiate rate in RoE. +- `-Pn` when ICMP is filtered; combine `-sn` on the whole CIDR first to find live hosts cheaply. +- UDP top-100 minimum: `nmap -sU --top-ports 100 -sV -O` — SNMP/DNS/mDNS/NTP/LDAP often missed. + +## Service enrichment +- **NSE**: `--script vuln,default` for triage; dangerous scripts (`brute`, `dos`, `exploit`) ONLY with explicit authorization. +- **SMB**: `nxc smb HOST` (shares, signing, OS, null session) → often instant finding (signing disabled = relay). +- **SNMP**: `snmpwalk -v2c -c public HOST .1.3.6.1.2.1` → network layout, VLANs, VPN peers. +- **TLS**: `testssl.sh HOST` — old TLS, weak ciphers, expired/expiring certs, heartbleed. + +## Web surface discovery +``` +# Content + vhosts + params +ffuf -u https://t/FUZZ -w raft-medium-directories -mc 200,204,301,302,401,403 +ffuf -u https://t/ -H "Host: FUZZ.t" -w subdomains.txt # vhost fuzzing +arjun -u https://t/page # hidden params +``` +- Status-code triage: 401/403 → bypass tests (`X-Original-URL`, method override); 301 to login = app surface. +- Mirror interesting dirs with `wget --mirror` or `katana` (crawl + JS link extraction) — JS files hold API routes and often tokens. +- API discovery: `/swagger`, `/openapi.json`, `/graphql`, `/.well-known/`, `/api/v{1..10}/`. + +## Tech stack +`whatweb -v URL` / `httpx -td` → CMS-specific scans (wpscan), framework CVE lookup, then version-match against Nuclei templates. + +## Gotchas +- Scan results without version detail are wasted — always `-sV`. A raw open port is not a finding. +- Load `nuclei` DURING this phase, not later: framework RCEs (Log4j, Spring4Shell, CVE-2021-*) don't show up in dir-fuzz. +- Deduplicate into engagement state via `nmap_parse` / `nuclei_parse` tools — the exploit phase queries `state_query`, it does not re-scan. diff --git a/skills/phases/exploitation/SKILL.md b/skills/phases/exploitation/SKILL.md new file mode 100644 index 00000000..7e8305c6 --- /dev/null +++ b/skills/phases/exploitation/SKILL.md @@ -0,0 +1,39 @@ +--- +name: exploitation-phase +tags: [exploitation] +description: Exploitation phase — prove impact safely. Router to web-* skills plus network, credential and wireless exploitation. Use when the current phase is EXPLOITATION. +--- + +# Exploitation — Prove Impact, Don't Break Things + +Golden rule: the goal is a DEMONSTRATED impact for the report, not maximum damage. Prefer the least destructive proof (file read > RCE > data exfil). + +## Web → load the matching `web-*` skill +SQLi (`web-sqli`), SSTI (`web-ssti`), SSRF (`web-ssrf`), LFI (`web-lfi-traversal`), upload RCE (`web-upload-rce`), XXE (`web-xxe`), auth bypass/IDOR (`web-auth-bypass-idor`), deserialization (`web-deserialization`). Router + payload index: `playbook-webapp`. +- `sqlmap --batch --level 3 --risk 2` only after a manual injection point is confirmed. +- Public PoC for a known CVE: fetch, READ IT, adapt to the exact version — never blind-run exploit binaries. + +## Network +- **Capture**: `tcpdump -i eth0 -w cap.pcap 'host X'` / Wireshark — creds in cleartext protocols (FTP, HTTP basic, SNMP) are findings on their own. +- **MITM/relay**: ARP spoofing only on flat L2 segments with authorization; NTLM relay requires SMB signing disabled (checked in enumeration). +- Responder + ntlmrelayx are the standard authorized chain: LLMNR/NBT-NS poison → relay to SMB/ADCS/LDAP. +- Impacket suite covers the rest: `GetNPUsers.py` (AS-REP), `GetUserSPNs.py` (kerberoast), `secretsdump.py`, `psexec.py`/`wmiexec.py` (lateral). + +## Credential attacks — lockout-aware ALWAYS +``` +# Spray: ONE password across MANY users, never the reverse +nxc smb DC -u users.txt -p 'Summer2024!' --continue-on-success +hydra -L users.txt -p 'CommonPass1!' target https-post-form '/login:user=^PASS^:F=invalid' +``` +- Respect lockout policy (enumerate it via `netexec ... --pass-pol` first). A locked-out user base = failed test. +- Cracking offline: `hashcat -m 1000` (NTLM), `-m 18200` (AS-REP), `-m 13100` (TGS), `-m 22000` (WPA). John as fallback. Rockyou + rules; custom wordlist from client context (org name, products, local slang). + +## Wireless +- Monitor-mode capture → `aircrack-ng -w rockyou cap.cap` (WPA2 handshake) or PMKID (`hcxdumptool`). +- Evil twin / deauth = disruptive; ONLY with written authorization, never on client production SSIDs during business hours. + +## Exploitation hygiene +- Verify scope (`scope_check`) before launching any exploit — payload callbacks to out-of-scope infra are a RoE breach. +- One shell per host is enough; unstable public PoCs → prefer manual exploitation for reliability. +- Log the exact command + timestamp as evidence at success time. +- If a chain caused unexpected impact (crash, data corruption): STOP, note it, disclose in report. Never hide it. diff --git a/skills/phases/recon/SKILL.md b/skills/phases/recon/SKILL.md new file mode 100644 index 00000000..54d74623 --- /dev/null +++ b/skills/phases/recon/SKILL.md @@ -0,0 +1,31 @@ +--- +name: recon-phase +tags: [recon] +description: Reconnaissance phase — passive OSINT then active enumeration. Use when the current phase is RECON. Triggers - OSINT, subdomain, whois, certificate transparency, shodan, google dork, footprint. +--- + +# Reconnaissance — Passive FIRST, then Active + +Rule: exhaust PASSIVE before touching the target. Passive leaves no logs on target infra; active does. + +## Passive +- **DNS/subdomains**: `subfinder -d target.com -silent`, `amass enum -passive -d target.com`, `assetfinder --subs-only target.com` +- **Cert transparency**: `curl -s "https://crt.sh/?q=%25.target.com&output=json" | jq -r '.[].name_value' | sort -u` — often finds forgotten subdomains + internal hostnames in CN fields +- **WHOIS/RDAP**: registrar, name servers, historical contacts (who.is, rdap.org) +- **Net blocks**: `whois -h whois.radb.net -- '-i origin ASXXXX'` → prefix list, then validate with `asnmap` +- **Shodan/Censys**: exposed services, banners, SSL certs (Shodan cert search pivots to sibling hosts on same org), leaked `.env`/`.git` indexes +- **Google dorks**: `site:target.com filetype:xlsx`, `site:target.com inurl:admin`, `intitle:"index of" site:target.com`, `site:target.com ext:env | ext:sql | ext:bak` +- **Code leaks**: GitHub/GitLab search `org:target`, `target.com password`, secret scanning (trufflehog/gitleaks on found repos) +- **Social**: LinkedIn (org chart, tech stack, employees for phishing/spray), job ads (tech in use), Wayback Machine for dead endpoints still live internally + +## Active (in-scope domains/IPs ONLY) +- **DNS brute + zone transfer**: `dig axfr target.com @NS`, `gobuster dns -d target.com -w wordlist` +- **Fingerprint**: `httpx -l subs.txt -status-code -title -tech-detect -favicon-hash` — favicon hash pivots to `Shodan http.favicon.hash:` +- **ASN/IP expansion**: resolve all found hosts → new IPs → re-check every IP against scope with `scope_check` +- **WAF detect**: `wafw00f https://target.com` — affects scan tuning later + +## Gotchas +- Subdomain takeover check immediately: dangling CNAME (npm/github/azure/s3) → `subjack`/`nuclei -t takeovers/`. It's recon AND a finding. +- A cert in CT logs is NOT proof the host is in scope. Validate + `scope_check` before any active probe. +- Record everything into the engagement state (`state_update`): hosts, domains, org relationships. Later phases consume this — a host found in recon but never scanned is the classic miss. +- Diff CT logs against live DNS: dead names may still resolve on INTERNAL resolvers (VPN/recon from inside). diff --git a/skills/phases/vuln-assess/SKILL.md b/skills/phases/vuln-assess/SKILL.md new file mode 100644 index 00000000..4e79aad0 --- /dev/null +++ b/skills/phases/vuln-assess/SKILL.md @@ -0,0 +1,40 @@ +--- +name: vuln-assess-phase +tags: [vuln_assess] +description: Vulnerability analysis — triage scanner output, verify manually, score with CVSS, mark false positives. Use when the current phase is VULN_ASSESS. +--- + +# Vulnerability Analysis — Scan, Verify, Score + +Raw scanner output is 80% noise. This phase exists to VERIFY and PRIORITIZE, not to collect CVEs. + +## Automated scan +``` +nuclei -u TARGET -severity critical,high -t ~/nuclei-templates/ -json -o nuclei.json +# authenticated nuclei when creds exist (10x coverage): -H 'Authorization: Bearer ...' +nikto -h https://target -Format json -o nikto.json # web misconfig baseline +``` +- Authenticated scans > anonymous scans everywhere (Nessus/OpenVAS/Nuclei): login first, then scan. +- Parse into state with `nuclei_parse` — dedupes and links vulns to hosts automatically. + +## Manual verification — MANDATORY before marking confirmed +Every scanner hit gets one of: +1. **Confirmed** — reproduce manually, capture request/response as evidence +2. **False positive** — record WHY (version patched, control in place); don't just delete +3. **Needs auth/context** — defer to exploitation phase with the required creds attached + +Classic FP sources: version banners lying (custom builds), WAF returning 200 for everything, template regex matching error pages. + +## CVSS scoring for prioritization +`CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H` = 9.8 Critical +- Prioritize by: exploitability (AV:N + no auth + public PoC) × impact, not by CVSS alone. +- A 9.8 unauthenticated RCE beats a 9.1 post-auth one. An exposure with active in-the-wild exploitation (CISA KEV) beats both. + +## Correlation across findings +- 3 "medium" findings chaining to RCE = one "critical" attack path (`attack_path_suggest`). +- Info leaks (versions, stack traces, .git exposure) feed other findings — chain them, don't score them isolated. + +## Gotchas +- Never report a vuln you could not reproduce — the retest will destroy client trust. +- Keep scanner timing/rate in evidence: a DoS-looking outage during scan ruins the report. +- Store evidence (PoC requests, screenshots) at confirmation time, not at reporting time. diff --git a/skills/playbooks/api/SKILL.md b/skills/playbooks/api/SKILL.md new file mode 100644 index 00000000..eca4babd --- /dev/null +++ b/skills/playbooks/api/SKILL.md @@ -0,0 +1,53 @@ +--- +name: playbook-api +description: API security testing — OWASP API Top 10, BOLA/IDOR chains, mass assignment, excessive data exposure, rate limiting. Load when target is a REST/GraphQL/JSON API (incl. mobile backend). +tags: [vuln_assess, exploitation] +--- + +# API Pentest — OWASP API Top 10 + +APIs fail differently than web apps: no UI to fuzz, auth is token-based, objects are addressable by ID. Test the OBJECT layer, not just the endpoint. + +## #1 BOLA / IDOR — test EVERY object reference +The single most common API finding. For each endpoint returning object data: +1. Grab a valid object ID (your own, from responses) +2. Swap in another user's ID — same token +3. Also try: sequential IDs, UUID prediction, deleted IDs, other ID formats +``` +# automate once endpoints are known +for id in $(seq 1 500); do + curl -s -H "Authorization: Bearer $TOK" https://api.t/v1/orders/$id | head -c 200 +done +``` +- Horizontal (same role, other user) AND vertical (user token → /admin/*) both ways. +- GraphQL: query nesting abuse — `{ user(id: 1) { friends { ssn } } }`, batching attacks. + +## #2 Broken auth on API +- Token: no expiry, no rotation, `alg:none`, JWT key confusion, refresh token reuse detection absent. +- OAuth: redirect_uri manipulation, `state` missing, scope escalation. +- Password reset flows keyed by enumerable identifiers. + +## #3 Mass assignment +Send extra fields in the create/update body: `{"role":"admin","verified":true,"balance":9999}`. Frameworks bind blindly (Rails `attr_accessible`, Spring `@ModelAttribute`, Mongoose strict:false). Test on register, profile update, and EVERY PATCH. + +## #4 Excessive data exposure +Compare response schemas between users. Common leaks: password hashes, internal IDs, other users' PII in list endpoints, `debug:true` payloads, full object when UI shows 3 fields. Diff admin-vs-user responses field by field. + +## #5 Rate limiting & resource abuse +- Login/OTP endpoints without lockout → brute force / SMS bombing (report, don't run to exhaustion). +- Unbounded pagination (`?limit=1000000`), unbounded file upload, unauthenticated expensive queries (ReDoS, GraphQL depth). + +## #6 Injection surface +- Params in URL, headers (`X-Forwarded-For`, custom trace headers), and JSON body — all three. +- SSTI/NoSQL/SQLi/SSRF payloads adapted per content-type; `Content-Type` switch (json→xml) can unlock XXE. + +## Methodology order +1. Map: `/openapi.json`, `/swagger.json`, `/graphql` introspection, mobile app traffic (pair with `svc-mobile-android`) +2. Enumerate object IDs per resource → BOLA sweep +3. Authz matrix: role × endpoint (collect tokens for each role available) +4. Mass assignment on every write endpoint +5. Response schema diff per role +6. Rate limits, injection, transport (TLS, cert pinning on mobile side) + +## Evidence for report +One curl + response pair per finding, showing: victim object belongs to user A, attacker token = user B. Include the exact reproduction steps — API findings retest easily, keep them scriptable. diff --git a/skills/playbooks/cloud/SKILL.md b/skills/playbooks/cloud/SKILL.md index 84d0b0bc..ef034935 100644 --- a/skills/playbooks/cloud/SKILL.md +++ b/skills/playbooks/cloud/SKILL.md @@ -43,3 +43,8 @@ Tools: `pacu` (automated), `pmapper` (permission graph) - User-data scripts (bootstrap credentials) - MFA not enforced on admin accounts - Audit logging disabled or misconfigured + +## Post-creds audit tools +- **AWS**: `pacu` (exploitation framework, modular), `pmapper` (privesc graph) +- **Multi-cloud posture**: `scoutsuite` (inventory + misconfig report per provider), `prowler` (AWS/GCP/Azure compliance checks, CIS benchmarks) +- Run posture scan FIRST after getting creds — misconfig findings often beat privesc chains. diff --git a/skills/playbooks/methodology/SKILL.md b/skills/playbooks/methodology/SKILL.md new file mode 100644 index 00000000..91a85879 --- /dev/null +++ b/skills/playbooks/methodology/SKILL.md @@ -0,0 +1,45 @@ +--- +name: playbook-methodology +description: Engagement-level methodology reference — PTES/OWASP/NIST/OSSTMM mapping, rules of engagement, legal boundaries, evidence handling, cleanup. Load at engagement START or when scope/authorization questions arise. +tags: [scope_check] +--- + +# Methodology & Rules of Engagement + +## Framework map (pick per engagement type) +| Framework | Use when | +|---|---| +| **PTES** | Full-scope pentest — the 7-phase spine (intel → recon → mapping → vuln → exploit → post-ex → report) | +| **OWASP Testing Guide** | Web/API focus — test cases per vuln class | +| **NIST SP 800-115** | Client requires a recognized US standard / structured technical assessment | +| **OSSTMM** | Metrics-driven assessment, need auditable coverage percentages | +| **MITRE ATT&CK** | Map findings to real adversary TTPs — report impact as "an attacker would do X" | + +## BEFORE any action — non-negotiable +1. **Written authorization** exists, signed by someone who can authorize the target. Verbal = no test. +2. **Scope**: exact IPs/domains/ranges, exclusions, test window, allowed techniques. Feed to `scope_check` — every tool invocation checks against it. +3. **Rules of engagement**: scan rates, DoS limits, social-engineering allowed?, physical allowed?, data handling clause, emergency contact. +4. **Third parties**: cloud shared-responsibility boundaries, subsidiaries, partners' infra — out of scope unless explicitly named. +5. Legal frame: client contract + local law (Convention Budapest / national cybercrime law) supersedes any methodology. + +## During the test +- Log every action (command, timestamp, target) — your log is the defense if something breaks. +- Rate-limit scanners and brute force; assume any outage is YOURS until proven otherwise. +- Collect the minimum data needed to prove impact; encrypt findings at rest; never exfil real customer data to personal storage. +- Compromise of an out-of-scope system, even accidental: stop, secure it, disclose to client immediately. +- Out-of-band callbacks: use your own infrastructure (interactsh/ollin), never third-party domains. + +## AFTER the test — cleanup +- Remove: shells/backdoors, dropped files, created accounts, cron/persistence, SSH keys, test data injected. +- Verify cleanup: re-check every host touched; note anything that cannot be removed safely (and why). +- Revoke/deliver credentials found — do NOT reuse them post-engagement. + +## Report shape (PTES-aligned) +1. Executive summary (business language, no vuln names) +2. Findings: severity (CVSS + exploitability), evidence, step-by-step reproduction, remediation +3. Attack narrative: how the kill chain connected findings (ATT&CK-mapped) +4. Coverage & methodology (what was and was NOT tested) +5. Remediation roadmap prioritized by risk, not by tool order + +## Retest +Fixes → retest ONLY the affected findings → upgrade/downgrade status in engagement state (`state_update`) → short addendum report. diff --git a/skills/playbooks/social-engineering/SKILL.md b/skills/playbooks/social-engineering/SKILL.md new file mode 100644 index 00000000..811c8961 --- /dev/null +++ b/skills/playbooks/social-engineering/SKILL.md @@ -0,0 +1,40 @@ +--- +name: playbook-social-engineering +description: Social engineering assessment — phishing campaigns, vishing, physical tests, GoPhish setup. Load when the engagement scope explicitly includes social engineering or physical testing. +tags: [vuln_assess] +--- + +# Social Engineering — Phishing, Vishing, Physical + +HARD GATE: this playbook requires the RoE to EXPLICITLY name social engineering/physical testing, the exact target population size, and data-handling rules. If the scope is silent → DO NOT run, ask the operator to confirm authorization. + +## Phishing (GoPhish) +1. **Pretext selection** from OSINT (recon phase): org tools, HR processes, recent events (reorg, new VPN, survey, invoice). +2. **Infrastructure**: own domain similar to org's, SPF/DKIM/DMARC properly set (deliverability), dedicated sending IPs warmed up, URL shorteners avoided (flagged). +3. **Payload tiers** (escalate only if authorized): credential harvest page (clone org login, HTTPS, valid cert) → macro doc → link to landing. +4. **Campaign metrics**: sent / opened / clicked / submitted / creds validated. Report EACH — click-through is the real risk indicator, not submissions. +5. **Timing**: Tue-Thu 09:00-11:00 local typically highest engagement; avoid month-end (finance busy, but BEC tests may target it deliberately — RoE call). + +## Vishing +- Same pretext as the phishing campaign; call targets who clicked but didn't submit ("IT follow-up"). +- OTP/2FA social-engineering: "read me the code you just received" is the highest-impact finding possible — proves full account takeover. +- Record calls ONLY if RoE allows (consent laws vary by jurisdiction — check two-party consent). + +## Physical +- Tailgating at badge-controlled entrances, badge cloning (Proxmark), lock picking, dumpster diving, USB drops. +- Escort rules and safety: never confrontation, abort word agreed, no bypassing locks that damage property. +- Environmental findings count: whiteboards, unlocked screens, unattended reception, printed creds. + +## Reporting angle +- Map each success to impact: "phished creds → mailbox access → BEC risk" with ATT&CK T1566/T1534/T1556. +- Compare results by department — one department at 40% submission rate = targeted training recommendation. +- NEVER store harvested real credentials beyond the engagement; hash/redact in report, deliver for reset. + +## GoPhish quick start +``` +gophish serve # admin panel, default :3333 +# landing page: clone org login (view-source → save), strip JS noise +# email template: org branding + tracking pixel +# sending profile: SMTP creds per RoE (dedicated domain, not client's) +``` +Results CSV → engagement state as findings, not spreadsheet-only. diff --git a/skills/services/mobile-ios/SKILL.md b/skills/services/mobile-ios/SKILL.md new file mode 100644 index 00000000..929b678e --- /dev/null +++ b/skills/services/mobile-ios/SKILL.md @@ -0,0 +1,38 @@ +--- +name: svc-mobile-ios +description: iOS IPA static analysis — hardcoded secrets, ATS bypass, Keychain, jailbreak detection, reverse engineering with class-dump/ipsw. Use when target is an IPA/iOS app. Triggers - IPA, iOS, Mach-O, class-dump, keychain, ATS, TestFlight. +--- + +# iOS IPA Static Analysis + +Pair with `svc-mobile-android` concepts — same audit axes, Apple-specific mechanics. + +## Reverse engineering +``` +# Decrypt (FairPlay) if App Store binary: ipsw / clutch / frida-ios-dump +# Class/object dump +class-dump -H BinaryName # headers from Mach-O +Hopper / Ghidra for control flow +strings -a BinaryName | grep -iE 'key|token|secret|password|api' +otool -l BinaryName | grep -A4 LC_ENCRYPTION_INFO # cryptid 1 = still encrypted +``` + +## Checklist (mirrors OWASP Mobile Top 10) +1. **Secrets**: hardcoded API keys in `Info.plist`, `*.strings`, binaries (strings + nm). Filter bundle IDs / public Firebase keys as non-issues — private keys and backend secrets count. +2. **ATS**: `NSAllowsArbitraryLoads=true` in Info.plist = cleartext allowed (High). Per-domain exceptions → note which domains. +3. **Keychain**: what's stored, with which accessibility (`kSecAttrAccessibleAlways` = leak on jailbreak). Data Protection class per file (NSFileProtectionComplete?). +4. **Local storage**: UserDefaults / SQLite / CoreData / Realm for tokens & PII — unencrypted = finding. +5. **Jailbreak detection**: file checks, scheme checks, fork() — document bypassability (Frida bypass = detection is bypassed, still note it as a control). +6. **Reverse-tamper**: only jailbreak checks, no integrity/signature verification of own binary? +7. **Exported schemes**: `CFBundleURLTypes` — custom URL handlers accepting unvalidated input (deep-link injection, auth bypass like `myapp://reset?token=`) +8. **Pasteboard / screenshots**: sensitive screens with `isSecureTextEntry` missing, screenshot in app switcher. +9. **Transport**: certificate pinning present? (check for AFSecurityPolicy/TrustKit/pinning libs — absence on a banking/health app = finding) +10. **Privacy manifests**: `NSUserTrackingUsageDescription`, excessive `NS*UsageDescription` vs actual functionality. + +## Dynamic (when in scope) +Frida: `frida -U -f com.bundle.id -l script.js` — hook NSURLSession to log traffic, bypass pinning (`ssl-kill-switch2`), dump Keychain. + objection: `objection explore` — root/jailbreak detection bypass, clipboard monitor, cookie dump. + +## Report notes +- iOS findings often: ATS off, secrets in binary, weak jailbreak detection, unencrypted local data. +- Distinguish TestFlight/dev builds (debug features expected) from production App Store binaries.