domains / security / source-leak-recon / hunt-source-leak
Both copies describe the same hunt-source-leak procedure (quick-win path probes, source map discovery, Swagger/OpenAPI discovery, secret grep), but each contains material steps the other lacks. The uphiago copy adds a Pitfalls section (fake source maps served as HTML, BusyBox/Alpine grep -P fallback, crt.sh rate limiting) and a reference to a React extraction pipeline. The sickn33 copy adds a mandatory authorization confirmation gate and a critical operational rule to re-derive content-hashed bundle names live because a 404 on an old .map URL does not mean remediation. Crowning one copy would hide either the environment pitfalls or the authorization/build-rotation constraints.
| Repository | Path |
|---|---|
| uphiago/recon-skills | redteam/hunt-source-leak/SKILL.md |
| sickn33/agentic-awesome-skills | plugins/agentic-awesome-skills-claude/skills/hunt-source-leak/SKILL.md |
| Role | Repository | Path |
|---|---|---|
| peer | uphiago/recon-skills | redteam/hunt-source-leak/SKILL.md |
| peer | sickn33/agentic-awesome-skills | plugins/agentic-awesome-skills-claude/skills/hunt-source-leak/SKILL.md |
| adapter | sickn33/agentic-awesome-skills | plugins/agentic-awesome-skills/skills/hunt-source-leak/SKILL.md |
| adapter | sickn33/agentic-awesome-skills | skills/hunt-source-leak/SKILL.md |
---
name: hunt-source-leak
description: Hunt source code and build artifact leakage — JavaScript source maps, Swagger/OpenAPI JSON, .env/.git exposure, webpack chunks, robots.txt/security.txt, build-info files, asset-manifest.json, .DS_Store. Use at the START of every recon session.
version: 1.2.0
license: MIT
category: security
risk: offensive
tags: [source, leak, hunt, redteam, security]
---
# HUNT-SOURCE-LEAK — Source Code & Build Artifact Leakage
## Authorization Gate (Mandatory)
Before running any command that probes a target:
1. Ask the user to state the exact target URL, IP, account, or resource.
2. Ask the user to confirm written authorization and the permitted scope.
3. Show the exact command(s) and explain their expected effect.
4. Wait for explicit confirmation in the current conversation.
Without that confirmation, remain read-only and provide defensive guidance only. Prefer a sandbox, disposable VM, or controlled lab.
## Crown Jewel Targets
Source map exposing TypeScript source = see all API routes, auth logic, secrets. Swagger/OpenAPI JSON = complete API surface map.
**Highest-value findings:**
- `.js.map` source maps — reconstruct full TypeScript/ES6 source code → find hardcoded API keys, internal endpoints, auth logic bypasses
- `swagger.json` / `openapi.json` — complete REST API specification with all endpoints, parameters, auth schemes, and internal route names
- `.env` / `.env.production` — APP_KEY, DB_PASSWORD, API_KEY, SECRET_KEY in plaintext
- `.git/` exposure — `git clone` the entire source history → all past hardcoded secrets
- `asset-manifest.json` / `_next/static/` — all JS bundle paths → systematic source map discovery
- `build-info` / `info.json` — git commit hash, build timestamp, dependency versions → CVE targeting
## Pitfalls (Read Before Probes)
### 1. Fake Source Maps — HTML Serving as .map
Some SPAs serve `index.html` for any unhandled route, including `.js.map` URLs. The URL ends in `.map` and returns HTTP 200, but the content is HTML.
**Detect before processing:**
```bash
head -c 80 /tmp/map_file
# "<!DOCTYPE" or "<html" = faux map (SPA serving HTML)
# "{" or ")))}" = real source map
python3 -c "
with open('/tmp/map_file') as f:
d = f.read()
if d.strip().startswith('{'): print('Real source map')
elif '<!DOCTYPE' in d or '<html' in d: print('FAUX — SPA serving HTML')
else: print(f'Unknown, first 80b: {repr(d[:80])}')
"
```
**Fallback:** Download raw JS bundles and grep directly.
### 2. BusyBox / Alpine grep -P
On minimal containers (Alpine Linux), `grep -P` is NOT available. Use Python3 `re` instead:
```bash
python3 -c "import sys, re; print(*(re.findall(r'pattern', sys.stdin.read())), sep='\n')" < file
```
### 3. crt.sh Rate Limiting
The crt.sh API rate-limits aggressively. Wait 10-15s between queries. Use `&limit=20` to reduce response size: `https://crt.sh/?q=%25.TARGET&output=json&limit=20`
### 4. Content-Hashed Bundle Rotation
Always resolve the CURRENT build hash before testing, and again before re-verifying. Bundle filenames are content-hashed and rotate on every deploy. A `.map` URL recorded yesterday can 404 today while the map is still fully exposed under a new name. **A 404 at the old URL is not remediation** — it is a new build.
```bash
HASH=$(curl -s "https://$TARGET/" | grep -oE 'main\.[a-f0-9]+\.js' | head -1)
curl -s -o /dev/null -w '%{http_code} %{size_download} %{content_type}\n' \
"https://$TARGET/static/js/${HASH}.map"
```
Tell the client explicitly in the report: **redeploying does not fix source map exposure.** Only `GENERATE_SOURCEMAP=false` (or stripping `.map` at deploy) plus a CDN purge closes it. Same rule applies to any content-hashed artifact: chunk files, CSS maps, `asset-manifest.json`, and staging equivalents.
## Phase 1 — Quick Wins (Run First)
```bash
for PATH in \
"/.env" \
"/.env.production" \
"/.env.local" \
"/.git/HEAD" \
"/swagger.json" \
"/api/swagger.json" \
"/v1/swagger.json" \
"/openapi.json" \
"/api/openapi.json" \
"/api-docs"; do
STATUS=$(curl --max-time 30 --connect-timeout 10 -s -o /tmp/sl_test -w "%{http_code}" "https://$TARGET$PATH")
if [ "$STATUS" = "200" ]; then
echo "[+] HIT: https://$TARGET$PATH"
head -5 /tmp/sl_test
echo "---"
fi
done
```
## Phase 2 — Source Map Discovery
See `references/react-api-extraction.md` for the full React SPA source-map API extraction pipeline.
```bash
# Step 1: Get asset manifest to find all JS bundle paths
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/asset-manifest.json" | python3 -m json.tool 2>/dev/null
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/static/js/main.*.js" 2>/dev/null | head -3
# Next.js
BUILD_ID=$(curl --max-time 30 --connect-timeout 10 -s https://$TARGET/ | grep -Eo '"buildId":"\K[^"]+')
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/_next/static/$BUILD_ID/_buildManifest.js" | head -5
# Step 2: For each JS bundle, check for source map reference at end of file
for JS_URL in $(curl --max-time 30 --connect-timeout 10 -s https://$TARGET/ | grep -Eo 'src="[^"]*\.js"' | sed 's/src="//;s/"//'); do
LAST_LINE=$(curl --max-time 30 --connect-timeout 10 -s "https://$TARGET$JS_URL" | tail -1)
echo "$LAST_LINE" | grep -q "sourceMappingURL" && echo "[+] Source map: $JS_URL"
done
# Step 3: Download and reconstruct source from .map files
JS_URL="https://$TARGET/static/js/main.abc123.js"
MAP_URL="${JS_URL}.map"
curl --max-time 30 --connect-timeout 10 -s "$MAP_URL" | python3 -c "
import sys, json, os
data = json.load(sys.stdin)
sources = data.get('sources', [])
contents = data.get('sourcesContent', [])
for i, (src, content) in enumerate(zip(sources, contents)):
if content:
path = '/tmp/sourcemap_extract/' + src.replace('../','').replace('./','').replace('webpack://','')
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, 'w') as f:
f.write(content)
print(f'[+] Extracted: {src}')
"
# Step 4: Grep extracted source for secrets
grep -r "API_KEY\|SECRET\|PASSWORD\|TOKEN\|PRIVATE" /tmp/sourcemap_extract/ 2>/dev/null
grep -r "process\.env\." /tmp/sourcemap_extract/ 2>/dev/null | grep -v "NEXT_PUBLIC_" | head -20
grep -r "http://internal\|localhost\|127\.0\.0\.1\|10\.\|172\.\|192\.168" /tmp/sourcemap_extract/ 2>/dev/null | head -20
```
## Phase 3 — Swagger / OpenAPI Discovery
```bash
SWAGGER_PATHS=(
"/swagger.json" "/swagger.yaml" "/swagger/"
"/api/swagger.json" "/api/swagger.yaml"
"/v1/swagger.json" "/v2/swagger.json" "/v3/swagger.json"
"/openapi.json" "/openapi.yaml"
"/api/openapi.json" "/api-docs" "/api-docs.json"
"/api/v1/swagger.json" "/api/v2/swagger.json"
"/rest/swagger.json" "/rest/api-docs"
"/.well-known/openapi.json"
"/graphql/schema.json"
)
for P in "${SWAGGER_PATHS[@]}"; do
STATUS=$(curl --max-time 30 --connect-timeout 10 -s -o /tmp/sw_test -w "%{http_code}" "https://$TARGET$P")
if [ "$STATUS" = "200" ]; then
echo "[+] Swagger/OpenAPI: https://$TARGET$P"
head -20 /tmp/sw_test
fi
done
```