When to use this skill
Trigger when:
- The target brand is a common/dictionary word or shared term (e.g.
apex, summit, vertex, nova, core, orbit, pulse, unity…)
- You receive an ASM report, recon export, breach combo, repo list, bucket list, or mobile-app list to act on
- A "Critical" count looks implausibly high (hundreds) for the org's size
- Any asset's ownership is asserted by the tool but not proven
The two failure modes this skill prevents:
- Wasting the engagement testing/triaging assets that aren't the target's.
- Attacking an innocent third party that merely shares the name — out of scope, and real harm.
Rule: ownership is guilty-until-proven. An asset is the target's only when a concrete ownership signal ties it to the target — never because a scanner's keyword matched.
The collision sources (where keyword-matching lies)
| Recon source |
How it collides |
Verify ownership by |
| GitHub repos |
Search matched the brand word in repo name / topic / a string |
Repo owner is the org's GH org; commits from org emails; code references the org's real domains/infra. A repo named <word>-backend by a random user = noise. |
| Cloud buckets (S3/GCS) |
Bucket names are a global namespace; <word>-static, <word>-data, <word>-public exist for someone |
Bucket content references the target; bucket name correlates with a confirmed target subdomain (x.target.com ↔ x-public) AND content matches; ACL/owner metadata. Generic content (other-language, other-industry) = not theirs. |
| Mobile apps |
Store search matched the brand word in app name / package |
Publisher account = the org; package reverse-DNS = an owned domain (com.<owneddomain>.app); dev cert; app calls owned API hosts. Mature ASM tools emit an "apps_accepted=0" / ownership-confidence field — read it. |
| Breach corpora / combos |
Email local-or-domain contains the brand word |
Exact owned-domain match only (@target.com), not @<word>group.com / @something<word>.com. A different domain that contains the word is a different org. |
| Typosquats |
Generated permutations of the name |
These are defensive/brand-protection findings, not offensive scope — note and move on. |
| Stack/forum/paste hits |
Brand word in body |
Body references the target's real domain/subdomain/employee/secret. Ownership-confidence < threshold = drop. |
Web "Critical" triage — the soft-404 control
Automated .env / .git / actuator / admin-panel "Criticals" are overwhelmingly soft-404s: SPA/framework catch-alls returning HTTP 200 (or 403) for every path. Verify EACH before believing it:
# the "finding"
curl -s -o /tmp/a -w "%{http_code} %{size_download}\n" https://host.target.com/.env
# a junk control on the same host
curl -s -o /tmp/b -w "%{http_code} %{size_download}\n" https://host.target.com/zzz-nonsense-$RANDOM
# identical byte length / body → FALSE POSITIVE (catch-all), discard
cmp -s /tmp/a /tmp/b && echo "SOFT-404 false positive" || echo "differs — investigate"
Real exposures have a content-type + signature that differs from the catch-all (.git/config starts [core]; .env has KEY=value; phpinfo has the XHTML-transitional doctype + PHP Version). A physical .php/phpinfo.php that returns a bigger/different body than the junk control is the real-vs-soft-404 tell.
The triage workflow
- Confirm the canonical owned-domain set first (the SOW/program domain + its verified subdomains + the verified Entra/Okta/Google tenant brand name). This is your ownership anchor.
- For each asset class, apply the verify-by column above. No signal → quarantine, don't test.
- Re-baseline the severity counts against only-owned assets. Report the delta — "N Criticals → M after ownership + soft-404 triage" is itself a finding about the ASM program.
- Quarantine collisions explicitly (a
loot/quarantined_<source>.txt) so it's auditable that you saw them and chose not to target them.
- Surface the meta-finding: if the supplied ASM/recon feed is mostly false-positive, that misallocates the owner's remediation budget and buries real risk — write it up (Medium/Strategic).
Anti-patterns
- Trusting the tool's "owned" label. Tools keyword-match; they don't prove ownership. Verify.
- Targeting a same-named third party because it was "in the report." Out of scope + real harm. A combo line
user@<word>company.com is a different company's employee.
- Reporting soft-404s as exposures. Always run the junk-path control.
- Counting typosquats / missing-headers / brand-collision repos as offensive findings. They're defensive/hygiene/noise — they pad the report and erode credibility.
- Skipping triage "to save time." Untriaged, you spend the whole engagement on other people's assets and find nothing real.
Why this matters (calibration)
For a target whose brand is a common word, expect the bulk of automated "owned" assets to be collisions:
- Repos that are unrelated open-source projects (ad-block lists, scrapers, student projects, a different company's SDK) merely containing the word.
- Mobile apps published by entirely different companies that share the name — banks, credit unions, dating apps, dispensaries, home-care services are all real-world collision categories. (Good ASM tooling will tell you it accepted zero as owned.)
- Cloud buckets in the global namespace holding some unrelated org's content (other-language documents, demo/sample data, another industry's files).
- Breach combos full of emails from sibling-named-but-different companies (
<word>group.com, <region><word>.com).
On a real engagement against a dictionary-word brand, after clearing this noise the only genuinely-owned high-severity finding was discoverable solely by manual tradecraft (a JS-bundle → API discovery, see hunt-spa-api) — it was nowhere in the hundreds of scanner "Criticals." Triage-first is what made the engagement productive instead of a goose chase.
Related Skills & Chains
triage-validation — asset-ownership triage (this skill) precedes finding-validity triage (the 7-Question Gate). Ownership first, then validity.
redteam-mindset — "aggressive default" means probe every owned live surface; this skill defines which surfaces are owned so persistence isn't wasted on collisions.
hunt-spa-api — once an API host passes ownership triage, this is how you test it.
offensive-osint / osint-methodology — feed ownership anchors (verified domains, tenant brand, dev accounts) from OSINT into this triage.
1---2name: recon-scope-triage3description: Triage ASM/recon output for ownership before testing — separate the target's real assets from namespace-collision noise. Automated recon keyword-matches on the brand name, so for any target whose name is a common/dictionary word, the output is dominated by assets belonging to UNRELATED same-named companies (repos, cloud buckets, mobile apps, breach corpora, typosquats). Built from an authorized engagement where an ASM report's "Criticals" were overwhelmingly false positives and the combo/repos/mobile/bucket lists were polluted with unrelated same-named orgs. Use at the START of any engagement, immediately on receiving any ASM/recon/OSINT dataset, BEFORE testing anything.4---5
6## When to use this skill
7
8Trigger when:
9- The target brand is a common/dictionary word or shared term (e.g. `apex`, `summit`, `vertex`, `nova`, `core`, `orbit`, `pulse`, `unity`…)
10- You receive an ASM report, recon export, breach combo, repo list, bucket list, or mobile-app list to act on
11- A "Critical" count looks implausibly high (hundreds) for the org's size
12- Any asset's ownership is asserted by the tool but not *proven*
13
14The two failure modes this skill prevents:
151. **Wasting the engagement** testing/triaging assets that aren't the target's.
162. **Attacking an innocent third party** that merely shares the name — out of scope, and real harm.
17
18**Rule: ownership is guilty-until-proven. An asset is the target's only when a concrete ownership signal ties it to the target — never because a scanner's keyword matched.**
19
20---
21
22## The collision sources (where keyword-matching lies)
23
24| Recon source | How it collides | Verify ownership by |
25|---|---|---|
26| **GitHub repos** | Search matched the brand word in repo name / topic / a string | Repo owner is the org's GH org; commits from org emails; code references the org's real domains/infra. A repo named `<word>-backend` by a random user = noise. |
27| **Cloud buckets** (S3/GCS) | Bucket names are a **global namespace**; `<word>-static`, `<word>-data`, `<word>-public` exist for *someone* | Bucket content references the target; bucket name correlates with a *confirmed* target subdomain (`x.target.com` ↔ `x-public`) AND content matches; ACL/owner metadata. Generic content (other-language, other-industry) = not theirs. |
28| **Mobile apps** | Store search matched the brand word in app name / package | Publisher account = the org; package reverse-DNS = an owned domain (`com.<owneddomain>.app`); dev cert; app calls owned API hosts. Mature ASM tools emit an "apps_accepted=0" / ownership-confidence field — read it. |
29| **Breach corpora / combos** | Email local-or-domain contains the brand word | **Exact** owned-domain match only (`@target.com`), not `@<word>group.com` / `@something<word>.com`. A different domain that contains the word is a different org. |
30| **Typosquats** | Generated permutations of the name | These are *defensive*/brand-protection findings, not offensive scope — note and move on. |
31| **Stack/forum/paste hits** | Brand word in body | Body references the target's real domain/subdomain/employee/secret. Ownership-confidence < threshold = drop. |
32
33---
34
35## Web "Critical" triage — the soft-404 control
36
37Automated `.env` / `.git` / `actuator` / admin-panel "Criticals" are overwhelmingly **soft-404s**: SPA/framework catch-alls returning HTTP 200 (or 403) for *every* path. Verify EACH before believing it:
38```bash
39# the "finding"
40curl -s -o /tmp/a -w "%{http_code} %{size_download}\n" https://host.target.com/.env
41# a junk control on the same host
42curl -s -o /tmp/b -w "%{http_code} %{size_download}\n" https://host.target.com/zzz-nonsense-$RANDOM
43# identical byte length / body → FALSE POSITIVE (catch-all), discard
44cmp -s /tmp/a /tmp/b && echo "SOFT-404 false positive" || echo "differs — investigate"
45```
46Real exposures have a content-type + signature that differs from the catch-all (`.git/config` starts `[core]`; `.env` has `KEY=value`; phpinfo has the XHTML-transitional doctype + `PHP Version`). A physical `.php`/`phpinfo.php` that returns a *bigger/different* body than the junk control is the real-vs-soft-404 tell.
47
48---
49
50## The triage workflow
51
521. **Confirm the canonical owned-domain set first** (the SOW/program domain + its verified subdomains + the verified Entra/Okta/Google tenant brand name). This is your ownership anchor.
532. **For each asset class, apply the verify-by column above.** No signal → quarantine, don't test.
543. **Re-baseline the severity counts** against only-owned assets. Report the *delta* — "N Criticals → M after ownership + soft-404 triage" is itself a finding about the ASM program.
554. **Quarantine collisions explicitly** (a `loot/quarantined_<source>.txt`) so it's auditable that you saw them and chose not to target them.
565. **Surface the meta-finding:** if the supplied ASM/recon feed is mostly false-positive, that misallocates the owner's remediation budget and buries real risk — write it up (Medium/Strategic).
57
58---
59
60## Anti-patterns
61
62- **Trusting the tool's "owned" label.** Tools keyword-match; they don't prove ownership. Verify.
63- **Targeting a same-named third party** because it was "in the report." Out of scope + real harm. A combo line `user@<word>company.com` is a different company's employee.
64- **Reporting soft-404s as exposures.** Always run the junk-path control.
65- **Counting typosquats / missing-headers / brand-collision repos as offensive findings.** They're defensive/hygiene/noise — they pad the report and erode credibility.
66- **Skipping triage "to save time."** Untriaged, you spend the whole engagement on other people's assets and find nothing real.
67
68---
69
70## Why this matters (calibration)
71
72For a target whose brand is a common word, expect the bulk of automated "owned" assets to be collisions:
73- **Repos** that are unrelated open-source projects (ad-block lists, scrapers, student projects, a different company's SDK) merely containing the word.
74- **Mobile apps** published by entirely different companies that share the name — banks, credit unions, dating apps, dispensaries, home-care services are all real-world collision categories. (Good ASM tooling will tell you it accepted *zero* as owned.)
75- **Cloud buckets** in the global namespace holding some unrelated org's content (other-language documents, demo/sample data, another industry's files).
76- **Breach combos** full of emails from sibling-named-but-different companies (`<word>group.com`, `<region><word>.com`).
77
78On a real engagement against a dictionary-word brand, after clearing this noise the only genuinely-owned high-severity finding was discoverable solely by manual tradecraft (a JS-bundle → API discovery, see `hunt-spa-api`) — it was nowhere in the hundreds of scanner "Criticals." Triage-first is what made the engagement productive instead of a goose chase.
79
80---
81
82## Related Skills & Chains
83
84- **`triage-validation`** — asset-ownership triage (this skill) precedes finding-validity triage (the 7-Question Gate). Ownership first, then validity.
85- **`redteam-mindset`** — "aggressive default" means probe every *owned* live surface; this skill defines which surfaces are owned so persistence isn't wasted on collisions.
86- **`hunt-spa-api`** — once an API host passes ownership triage, this is how you test it.
87- **`offensive-osint` / `osint-methodology`** — feed ownership anchors (verified domains, tenant brand, dev accounts) from OSINT into this triage.