mirror of
https://github.com/usestrix/strix.git
synced 2026-08-17 01:29:42 +02:00
Add npx/dependency name-confusion skill and restore concrete guidance
This commit is contained in:
@@ -42,12 +42,14 @@ Notable source-aware skills:
|
||||
- `source_aware_whitebox` (coordination): white-box orchestration playbook
|
||||
- `source_aware_sast` (custom): semgrep/AST/secrets/supply-chain static triage workflow
|
||||
- `dependency_cve_scanning` (custom): trivy-based SCA workflow for reporting known dependency CVEs via `create_dependency_report`
|
||||
- `supply_chain_name_confusion` (custom): npx/dependency confusion — command and package names the target resolves from a public registry but nobody owns, with non-destructive claimability verification and false-positive gates
|
||||
- `advisory_to_poc` (custom): advisory-to-root-cause workflow for patch diffing, public PoCs, and detector design
|
||||
- `appliance_firmware` (technologies): appliance artifact, runtime, and install-state analysis
|
||||
- `protocol_reverse_engineering` (protocols): stateful/custom protocol reconstruction and safe harnessing
|
||||
- `semantic_confusion` (vulnerabilities): cross-boundary parser, normalization, and representation mismatch analysis
|
||||
- `memory_corruption` (vulnerabilities): native crash triage, primitive quality, and exploitability constraints
|
||||
- `agentic_system_security` (vulnerabilities): effective-authority and MCP/tool ecosystem security testing
|
||||
- `browser_security` (vulnerabilities): browsing-context, postMessage, XS-Leaks, service-worker, and cross-origin state-machine testing
|
||||
- `azure` (cloud): Azure and Microsoft Entra privilege, PIM, workload identity, and cross-plane escalation analysis
|
||||
- `infrastructure_lifecycle` (reconnaissance): abandoned or mutable external dependencies such as update endpoints, MX, storage, and control domains
|
||||
|
||||
|
||||
@@ -129,6 +129,15 @@ In repositories with developer tooling, plugins, templates, or package runners,
|
||||
|
||||
Record candidate names and verify ownership/existence without claiming or publishing them. A namespace gap is reportable only when the target actually resolves or executes the attacker-contestable name under realistic conditions.
|
||||
|
||||
For the npm/JavaScript case this is decidable without publishing anything —
|
||||
`npx --no <cmd>` in a clean directory prints the exact registry URL npm would
|
||||
fetch, and the same command in the repo after `npm ci` shows whether
|
||||
`node_modules/.bin` already satisfies it. `load_skill(["supply_chain_name_confusion"])`
|
||||
for the full workflow (npx/bunx/`dlx` fallback, scoped-package `bin` mismatch,
|
||||
internal/`workspace:` deps, MCP configs) and its registry-status and
|
||||
claimability gates. Trivy cannot see any of this: the dependency is not
|
||||
vulnerable, the name resolution is.
|
||||
|
||||
Load `infrastructure_lifecycle` when source, images, firmware, or history contain abandoned domains, provider resources, package namespaces, update URLs, mail identities, telemetry, or control endpoints. Use targeted string/dataflow analysis when this is the research question; the full baseline scanner bundle is not required merely to trace one endpoint consumer.
|
||||
|
||||
## Secret and Supply Chain Coverage
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
---
|
||||
name: supply-chain-name-confusion
|
||||
description: npx/dependency confusion playbook — find command and package names a target resolves from a public registry but nobody owns (npx/bunx/dlx fallback, scoped-package bin mismatch, internal deps), verify claimability non-destructively, and report only proven-reachable cases
|
||||
---
|
||||
|
||||
# Supply Chain Name Confusion (npx / dependency confusion)
|
||||
|
||||
A target is vulnerable when a name it *executes or installs* is resolved from a
|
||||
**public** registry and that name is **not owned by the target**. Whoever
|
||||
registers the name first gets arbitrary code execution on developer laptops,
|
||||
CI/CD runners, release pipelines, and AI coding agents — with whatever tokens
|
||||
those environments hold. There is no CVE, no vulnerable version, and SCA tools
|
||||
miss it completely: the dependency is not vulnerable, the *name resolution* is.
|
||||
|
||||
Four distinct findings, in descending signal:
|
||||
|
||||
| Type | Condition |
|
||||
|---|---|
|
||||
| **npx confusion** | `npx <cmd>` where `<cmd>` is not resolvable locally, so npm installs the public package literally named `<cmd>` |
|
||||
| **bin mismatch** | A scoped package `@org/foo-tool` ships `"bin": {"foo-tool": ...}`. `bin` keys cannot contain `/`, so docs/scripts say `npx foo-tool` → resolves the **unscoped** name, which the org usually never registered |
|
||||
| **dependency confusion** | An internal/`workspace:`/`file:` dependency name that resolves publicly when the private registry is missing, misconfigured, or lower-priority |
|
||||
| **name clash** | The name exists publicly but is owned by an unrelated third party — the target already executes someone else's code |
|
||||
|
||||
This skill is **npm-name-resolution** focused, and deliberately concrete: the
|
||||
decision procedure below is what separates a real finding from an unowned name
|
||||
that nothing actually resolves. Related skills, and where the boundary sits:
|
||||
|
||||
- `dependency_cve_scanning` — known-CVE dependency versions. Different finding
|
||||
class, different report tool.
|
||||
- `infrastructure_lifecycle` — the general ownership-continuity model (domains,
|
||||
MX, update endpoints, buckets). Load it when the target trusts an abandoned
|
||||
*endpoint* rather than an unowned *name*.
|
||||
- `agentic_system_security` — MCP/agent component supply chain. Load it when the
|
||||
question is the agent's effective authority; come here for who owns the
|
||||
package name its `command: npx -y <name>` resolves.
|
||||
- `semantic_confusion` — the general "two components disagree about a
|
||||
representation" model, of which scoped-package-vs-unscoped-bin is one case.
|
||||
- CI/CD workflow abuse (`pull_request_target`, mutable PR merge refs, cache
|
||||
poisoning) is a separate class — do not fold it in here.
|
||||
|
||||
## How npx Resolves a Command
|
||||
|
||||
npm CLI (`libnpmexec`) tries, in order:
|
||||
|
||||
1. `node_modules/.bin` walking up from cwd (local install)
|
||||
2. the global bin dir / global `node_modules`
|
||||
3. the npx cache (`_npx`)
|
||||
4. **fetch from the configured registry** the package literally named after the
|
||||
command, install it, then execute its bin
|
||||
|
||||
Step 4 is the vulnerability. Two properties make it worse than it looks:
|
||||
|
||||
- In a **non-TTY / CI** context npm does not prompt — it logs a warning and
|
||||
installs. `-y` / `--yes` (extremely common in CI and in MCP server configs)
|
||||
removes the prompt everywhere.
|
||||
- The registry is hit **before** the prompt/`--no` check, so the resolution
|
||||
target is observable without ever installing anything.
|
||||
|
||||
Equivalents to cover: `npm exec`, `bunx`, `pnpm dlx`, `yarn dlx`,
|
||||
`deno run npm:<name>`. Same class in other ecosystems: `uvx <name>` /
|
||||
`pipx run <name>` (PyPI dist name vs `console_scripts` name — identical
|
||||
mismatch bug), implicit `docker.io/library/<image>`, devcontainer features,
|
||||
and `uses: org/repo@ref` in GitHub Actions when the org/repo was renamed.
|
||||
|
||||
## Phase 1 — Collect Candidates (with execution context)
|
||||
|
||||
Record for every candidate: **name, file, line, and whether it is executed**.
|
||||
Context is what separates a finding from noise later, so never collect a bare
|
||||
name list.
|
||||
|
||||
```bash
|
||||
ART=/workspace/.strix-namecheck; mkdir -p "$ART"
|
||||
|
||||
# Executed invocations — the primary vector
|
||||
rg -n --no-heading -g '!node_modules' \
|
||||
-e '\b(npx|bunx)\s+(-{1,2}[a-zA-Z-]+(=\S+)?\s+)*[@a-zA-Z0-9._/-]+' \
|
||||
-e '\b(pnpm|yarn)\s+dlx\s+\S+' \
|
||||
-e '\bnpm\s+exec\s+\S+' \
|
||||
. > "$ART/invocations.txt"
|
||||
```
|
||||
|
||||
Cover every executable surface, not just `package.json` scripts:
|
||||
|
||||
- `package.json` `scripts` (incl. `pre*`/`post*` hooks), `Makefile`, shell
|
||||
scripts, `Dockerfile` `RUN`, `.husky/`, `lint-staged`, Turbo/Nx task defs
|
||||
- CI: `.github/workflows/*.yml` `run:` steps, composite actions, GitLab CI,
|
||||
Jenkinsfiles — highest impact, these hold registry/cloud credentials
|
||||
- **MCP / agent configs**: `.mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json`,
|
||||
`claude_desktop_config.json`, `devcontainer.json`. These are almost always
|
||||
`npx -y <name>` and are executed by an agent with no human review
|
||||
- `README`/docs install snippets — real risk (humans and agents paste them),
|
||||
but lower confidence than a CI step; grade accordingly
|
||||
- `bin` maps of every package the target *publishes* (walk its npm scope:
|
||||
`https://registry.npmjs.org/-/org/<org>/package`), plus local workspace
|
||||
`package.json` files
|
||||
- `dependencies`/`devDependencies` entries using `file:`, `link:`,
|
||||
`workspace:`, or a scope that only exists internally
|
||||
- Black-box targets: exposed `/package.json`, `/package-lock.json`,
|
||||
`/npm-shrinkwrap.json`, source maps (`sources[]`), and webpack module paths
|
||||
(`node_modules/<name>/`) in served bundles. Capture bundles from real traffic
|
||||
(agent-browser HAR) rather than scraping HTML — lazily loaded chunks hold the
|
||||
internal names, and parse them as JS (AST) instead of regexing minified text
|
||||
|
||||
Drop immediately, before any registry traffic: Node builtins (`fs`, `node:*`),
|
||||
names containing shell/template expansion (`$VAR`, `{{`, backticks), paths
|
||||
(`./x`, `/x`), flags, and anything failing npm name rules (lowercase, ≤214
|
||||
chars, no leading `.`/`_`, URL-safe).
|
||||
|
||||
## Phase 2 — Prove the Name Reaches the Public Registry
|
||||
|
||||
Do this **before** checking availability. It is the gate that kills most noise:
|
||||
if the command resolves locally, there is no registry fallback and no finding.
|
||||
|
||||
```bash
|
||||
# A. What does the bare command actually resolve to? Clean dir, no deps.
|
||||
cd "$(mktemp -d)" && npx --no <cmd> 2>&1 | grep -E '404|GET https'
|
||||
# → "404 Not Found - GET https://registry.npmjs.org/<cmd>" proves npx maps the
|
||||
# bare command to exactly that public package. Non-destructive: npm resolves
|
||||
# the manifest before the install prompt, so nothing is installed.
|
||||
|
||||
# B. Does the target's own environment fall back? Repo root, after normal install.
|
||||
cd /workspace/<repo> && npm ci && npx --no <cmd> --version
|
||||
# → succeeds = satisfied by node_modules/.bin → NOT a finding in this context
|
||||
# → E404/ENOENT = falls back to the registry → finding stands
|
||||
```
|
||||
|
||||
`npx --no` from a subdirectory still walks up to the workspace root, so a
|
||||
locally installed bin is safe from any cwd inside the project. Treat a
|
||||
locally-satisfied command as a finding **only** when you can point at an
|
||||
execution path outside that installed tree (a bootstrap script run before
|
||||
`npm ci`, a docs snippet a user runs in `$HOME`, an MCP config on a developer
|
||||
machine) — and grade it lower.
|
||||
|
||||
## Phase 3 — Determine Ownership and Claimability
|
||||
|
||||
```bash
|
||||
curl -s -o /tmp/pkg.json -w '%{http_code}\n' \
|
||||
"https://registry.npmjs.org/$(printf '%s' "$NAME" | sed 's|@|%40|; s|/|%2f|')"
|
||||
```
|
||||
|
||||
| Response | Meaning | Action |
|
||||
|---|---|---|
|
||||
| `404 {"error":"Not found"}` | unregistered | candidate finding — continue to the gates below |
|
||||
| `200`, `dist-tags.latest` = `0.0.1-security` (single stub version, npm-owned) | npm **security holding** placeholder | **not claimable** → not a finding. Strong evidence the name was already abused/reserved; report at most informationally |
|
||||
| `200`, maintainers/repository belong to the target | target owns it | not a finding — drop silently |
|
||||
| `200`, unrelated owner | **name clash** — target executes third-party code | finding only if Phase 2 passed; check publish date, weekly downloads, and whether the code is malicious/squatting |
|
||||
| `401`/`403` | private or blocked scope | inconclusive — do not report |
|
||||
| `429`/`5xx`/timeout/DNS failure | **unknown** | never treat as unregistered; retry later, otherwise record as a scan limitation |
|
||||
|
||||
Two claimability gates that npxconfuse-style tooling gets wrong:
|
||||
|
||||
- **npm typosquat/moniker rule**: a *new* package name is rejected if, with
|
||||
punctuation (`. - _`) removed, it collides with an existing package. So a
|
||||
`404` name can still be unregisterable. Check the punctuation-stripped form
|
||||
and plausible punctuation variants; if one exists, the attack fails — downgrade
|
||||
to informational. `npm publish --dry-run` does **not** perform this check.
|
||||
- **Unpublished names**: `name@version` can never be reused, and a fully
|
||||
unpublished package's name is blocked for 24h. A `404` on a name whose GitHub
|
||||
history shows it once existed needs this called out in `assumptions`.
|
||||
|
||||
Verify the checker itself before trusting any `404` — a proxy, mirror, or
|
||||
offline sandbox can turn every lookup into a uniform answer:
|
||||
|
||||
```bash
|
||||
for n in lodash strix-sentinel-$(uuidgen | tr 'A-Z' 'a-z'); do
|
||||
echo "$n $(curl -s -o /dev/null -w '%{http_code}' https://registry.npmjs.org/$n)"
|
||||
done
|
||||
# MUST print 200 then 404. Anything else → your results are meaningless; stop.
|
||||
```
|
||||
|
||||
Re-confirm every `404` at least twice, spaced out, and cross-check with
|
||||
`npm view <name> versions --json`.
|
||||
|
||||
## Phase 4 — Report
|
||||
|
||||
**Never publish, reserve, or squat a name on a public registry**, and never
|
||||
ship a canary/callback payload — that is an attack on a third party and on the
|
||||
target's users. All evidence here is non-destructive. If the target explicitly
|
||||
authorizes a defensive placeholder publish, that is remediation work, not
|
||||
validation, and needs written authorization first.
|
||||
|
||||
File with `create_vulnerability_report`, one report per **unique name** with
|
||||
every occurrence aggregated in `code_locations` (never one report per line).
|
||||
|
||||
The PoC is the two-part resolution proof, which is fully reproducible and
|
||||
requires no malicious package: (1) `npx --no <cmd>` in a clean directory
|
||||
showing `GET https://registry.npmjs.org/<cmd>` → 404, i.e. the command resolves
|
||||
to an unowned public name; (2) the target's own execution path (CI step, MCP
|
||||
config, docs command) that runs it. State plainly in `assumptions` that code
|
||||
execution follows from an attacker registering the name and that no package was
|
||||
published during testing.
|
||||
|
||||
Severity — derive it from the environment that executes the command:
|
||||
|
||||
- **Critical/High**: unregistered name reached from CI/CD, release, or
|
||||
publish-time execution, or from an MCP/agent config or bootstrap script that
|
||||
runs on developer machines. Full code execution with pipeline credentials
|
||||
(`AV:N`, `PR:N`, `C:H/I:H/A:H`; `UI:R` when a human or agent must trigger it)
|
||||
- **Medium**: name clash — the target already executes an unrelated owner's
|
||||
package, but you have not shown that package is malicious
|
||||
- **Low/Informational**: docs-only mention with no executed path; the command
|
||||
is satisfied locally in every real execution context; the name is npm-held,
|
||||
blocked by the moniker rule, or private-scope
|
||||
- **Not a finding**: registry lookup inconclusive, target owns the name, or
|
||||
Phase 2 showed no registry fallback
|
||||
|
||||
## False Positives — Hard Gates
|
||||
|
||||
Every one of these has burned automated name-confusion scanners:
|
||||
|
||||
1. **Locally satisfied commands.** `npx tsc`, `npx eslint`, `npx vite` in a repo
|
||||
that declares them are resolved from `node_modules/.bin`. Phase 2B is
|
||||
mandatory, not optional.
|
||||
2. **Popular public tools.** A `200` for `prettier`/`tsc`/`jest` is the real
|
||||
tool by its real maintainer, not a clash. Do not report ecosystem-standard
|
||||
binaries as name clashes without evidence of ownership change.
|
||||
3. **Non-confusable invocation forms.** `npx @scope/pkg`, `npx --package
|
||||
@scope/pkg <bin>`, `npx pkg@1.2.3` where the org owns `pkg`, and any
|
||||
`--registry`-pinned or `.npmrc` scope-routed call. Regex-matching `npx \S+`
|
||||
flags all of these.
|
||||
4. **`.npmrc` scope routing.** If `@org:registry=` points at a private registry
|
||||
and the internal scope is fully qualified, dependency confusion for that
|
||||
scope does not apply. Read `.npmrc`, `.yarnrc.yml`, and CI registry setup
|
||||
before reporting internal-dependency findings.
|
||||
5. **Registry errors read as availability.** Rate limits, proxy blocks, and
|
||||
egress restrictions are not `404`. Run the sentinel check.
|
||||
6. **npm-held / unregisterable names.** `0.0.1-security` stubs and
|
||||
moniker-rule collisions look claimable but are not.
|
||||
7. **Names the target already owns.** Compare maintainers, repository URL, and
|
||||
scope before assuming a third party controls a name.
|
||||
8. **Bundle-extraction garbage.** Minified identifiers, CSS class names, and
|
||||
chunk fragments are not package names. Require an AST-level
|
||||
`require`/`import`/module-path context, then the npm-name-validity filter.
|
||||
9. **Upstream, not the target.** An unowned name inside a third-party
|
||||
dependency's manifest is that maintainer's exposure. Note it separately;
|
||||
do not file it against the target.
|
||||
10. **Duplicates.** Dedupe by name across all sources before any registry
|
||||
traffic and before reporting.
|
||||
|
||||
## Remediation
|
||||
|
||||
Ordered by durability:
|
||||
|
||||
1. Invoke the full package name: `npx --package @org/foo-tool foo-tool`, or
|
||||
rename the `bin` so it matches, or drop `npx` and call the local binary
|
||||
(`node_modules/.bin/foo-tool`, `npm run`).
|
||||
2. Add `--no` (`npx --no <cmd>`) so a missing local install fails loudly
|
||||
instead of silently installing from the public registry; use
|
||||
`npm ci`-installed devDependencies in CI, never on-the-fly `npx -y`.
|
||||
3. Register the unscoped names the org's docs and `bin` maps tell people to
|
||||
run, as placeholders pointing at the scoped package.
|
||||
4. Route internal scopes explicitly (`@org:registry=`) and ensure the private
|
||||
registry does not fall through to the public one for internal names.
|
||||
5. Pin versions, commit lockfiles, and prefer `--offline`/vendored installs in
|
||||
privileged pipelines.
|
||||
6. Audit MCP/agent configs for `npx -y <bare-name>` — an agent will run them
|
||||
without asking.
|
||||
@@ -69,6 +69,16 @@ Record signature, hash, certificate, pinning, version/rollback, and content-type
|
||||
|
||||
Do not register or publish candidate names merely to test them without explicit authorization and a containment plan. Prove the consumer's resolution behavior first.
|
||||
|
||||
Registry "missing" responses are not interchangeable with "claimable". On npm,
|
||||
a `404` name can still be unregisterable (the punctuation-stripping moniker
|
||||
rule blocks names that collide with an existing package once `.`/`-`/`_` are
|
||||
removed), and a `200` may be an npm `0.0.1-security` holding stub that nobody
|
||||
else can take. `load_skill(["supply_chain_name_confusion"])` for the npm/npx
|
||||
resolution order, the non-destructive `npx --no` reachability proof, and the
|
||||
full registry-status matrix; the same shape applies to `uvx`/`pipx run` on
|
||||
PyPI (distribution name vs `console_scripts` name), implicit
|
||||
`docker.io/library/<image>`, and `uses: org/repo@ref` after an org rename.
|
||||
|
||||
### Mail and Identity
|
||||
|
||||
- expired organizational, supplier, recovery, notification, or former employee domains
|
||||
@@ -201,7 +211,8 @@ Include:
|
||||
- Live requests go to a vendor-controlled wildcard/catch-all despite an apparently missing specific resource.
|
||||
- Update content is independently signed and the reassigned endpoint cannot produce an accepted artifact; this usually blocks forged-code impact, but metadata exposure, update suppression, unsigned manifest fields, and rollback/version behavior still require analysis.
|
||||
- Expired domain appears in documentation but is absent from authentication, mail, software, and deployed configuration.
|
||||
- A package name is unregistered but the consumer is pinned to a private registry with no public fallback.
|
||||
- A package name is unregistered but the consumer is pinned to a private registry with no public fallback, the scope is routed by `.npmrc`, or the command is already satisfied by a locally installed binary.
|
||||
- The name is unregistered but unregisterable: blocked by a registry similarity/moniker rule, held by a registry-owned security placeholder, or reserved after an unpublish.
|
||||
- Inbound sensor traffic cannot be attributed to an in-scope consumer/version.
|
||||
|
||||
## Remediation
|
||||
|
||||
@@ -120,6 +120,14 @@ Every skill, plugin, MCP server, model adapter, package, and update channel is a
|
||||
- transitive dependencies and lifecycle scripts
|
||||
- review/approval ownership and last verification date
|
||||
|
||||
Agent and MCP configs (`.mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json`,
|
||||
`claude_desktop_config.json`, `devcontainer.json`) are the highest-risk
|
||||
instance of this: a `command: npx` with `-y` and a bare, unscoped package name
|
||||
is an unattended registry install of whatever that name resolves to, with no
|
||||
human in the loop. Enumerate every such entry and confirm who owns the name
|
||||
— `load_skill(["supply_chain_name_confusion"])` for the resolution proof and
|
||||
ownership/claimability gates.
|
||||
|
||||
Test missing/private-name fallback, typosquatting exposure, mutable remote instructions, compromised-update blast radius, and whether an “instruction-only” component can invoke tools or modify executable files. Resolve `latest`, floating git refs, and mutable image tags to immutable versions or digests before launch. Do not claim or publish contestable package names as proof, and do not execute unknown packages just to discover what they are.
|
||||
|
||||
Load `infrastructure_lifecycle` when a skill, plugin, MCP server, model adapter, tool-schema origin, package namespace, or update endpoint is retired, mutable, or externally reassignable. Passive receipt of an agent heartbeat or catalog request does not authorize returning tool definitions, prompts, commands, or executable content.
|
||||
|
||||
@@ -106,13 +106,15 @@ The `X-Forwarded-*` family is informational — there is no protocol guarantee a
|
||||
- `X-Forwarded-For: 127.0.0.1` to bypass IP allowlists or rate limits keyed on client IP
|
||||
- `X-Forwarded-Proto: https` to satisfy "HTTPS-only" checks while still using HTTP
|
||||
- `X-Forwarded-Host: attacker.tld` for the Host-confusion variants above
|
||||
- `X-Real-IP`, `Client-IP`, `True-Client-IP`, `CF-Connecting-IP`, `Forwarded` (RFC 7239) — same trust class under different conventions; select evidence-supported variants for the observed proxy/CDN stack
|
||||
- `X-Real-IP`, `Client-IP`, `True-Client-IP`, `CF-Connecting-IP`, `Forwarded` (RFC 7239) — same trust class under different conventions; lead with the variants the observed proxy/CDN stack actually honors, but each one costs a single request, so spraying the whole list against an IP-gated endpoint is cheap
|
||||
- `X-Original-URL` / `X-Rewrite-URL` (IIS, ASP.NET) — server-side URL rewriting after auth check, classic admin-panel auth bypass
|
||||
|
||||
### Content-Type / Encoding Confusion
|
||||
|
||||
- Inject `Content-Type: text/html` into an endpoint that returned JSON; browsers may sniff and render → XSS
|
||||
- Inject `charset=utf-7` in `Content-Type` for XSS via UTF-7-encoded payloads on legacy stacks that still honor it
|
||||
- Inject `Content-Disposition: inline` to switch a download into in-page rendering
|
||||
- Inject `Content-Encoding: gzip` without actually compressing — clients decode-fail and may reveal raw response bytes in error paths
|
||||
- *Absence* of `X-Content-Type-Options: nosniff` is what enables the sniffing attacks above; the header is a hardening control, not an attack surface — but if a server sets it inconsistently across endpoints, target the ones that don't
|
||||
- Compare MIME validators with browser parsing of duplicate or comma-joined `Content-Type` values. Record first/last valid member behavior and invalid-parameter recovery for each consumer.
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ Improper file path handling and dynamic inclusion enable sensitive file disclosu
|
||||
### Capability Probes
|
||||
|
||||
- Path traversal baseline: `../../etc/hosts` and `C:\Windows\win.ini`
|
||||
- Encodings: `%2e%2e%2f`, `%252e%252e%252f`, `..%2f`, `..%5c`, and Unicode lookalikes only where a documented conversion layer maps them to path syntax
|
||||
- Encodings: `%2e%2e%2f`, `%252e%252e%252f`, `..%2f`, `..%5c`; overlong UTF-8 (`%c0%2e`, `%c0%af`) and Unicode dot/slash lookalikes (`.`, `/`, `%u2215`) only where a conversion layer in the stack maps them to path syntax — send them, but only claim a bypass once you see the converted path in the response or logs
|
||||
- Normalization tests: `..../`, `..\\`, `././`, trailing dot/double dot segments; repeated decoding
|
||||
- Absolute path acceptance: `/etc/passwd`, `C:\Windows\System32\drivers\etc\hosts`
|
||||
- Server mismatch: `/static/..;/../etc/passwd` ("..;"), encoded slashes (`%2F`), double-decoding via upstream
|
||||
@@ -82,7 +82,7 @@ Improper file path handling and dynamic inclusion enable sensitive file disclosu
|
||||
### Path Traversal Bypasses
|
||||
|
||||
**Encodings**
|
||||
- Single/double URL-encoding, mixed case, UTF-16 or Unicode conversion only when present in the stack, and path normalization oddities
|
||||
- Single/double URL-encoding, mixed case, and path normalization oddities; overlong UTF-8 and UTF-16/Unicode conversion (`%u002e`, `%c0%ae`) only when that conversion layer is present in the stack — IIS/legacy .NET, Java on Windows, and some CDN/WAF normalizers are the usual carriers
|
||||
|
||||
**Mixed Separators**
|
||||
- `/` and `\\` on Windows; `//` and `\\\\` collapse differences across frameworks
|
||||
|
||||
@@ -83,8 +83,9 @@ The highest-signal condition is `security_check(value_A)` followed by `sink(tran
|
||||
|
||||
- Identify names resolved across multiple scopes: local path, environment `PATH`, cache, private registry, public registry, plugin directory, template search path, or autoloader.
|
||||
- Record lookup order and what happens when the intended entry is missing.
|
||||
- Compare protected package/module names with exposed command, binary, handler, or alias names.
|
||||
- Compare protected package/module names with exposed command, binary, handler, or alias names. A scoped npm package cannot put `/` in a `bin` key, so `@org/foo-tool` ships the unscoped command `foo-tool` — the protected name and the invoked name differ by construction.
|
||||
- Treat automatic remote fallback or search-path fallback as an execution boundary.
|
||||
- Load `supply_chain_name_confusion` when the mismatched representation is a package/command name resolved from a registry; it carries the resolution-order proof and ownership gates for that case.
|
||||
|
||||
## Reconnaissance
|
||||
|
||||
|
||||
Reference in New Issue
Block a user