An evolving HTTP honeypot, actively maintained by an LLM working off observations from a live honeypot sensor network. Fresh scanner behaviour in the corpus drives new traps; existing traps get tuned or retired as the logs show what's eliciting follow-up and what's being ignored.
Small async Python behind nginx. Python 3.11+; one runtime dep — aiohttp. Async so the tarpit and fake-git drip paths can hold thousands of concurrent slow-drip connections at ~8 KB each instead of one OS thread each.
Experimental — no guarantees about safety or value. Every line was written by an LLM from natural-language prompts, smoke-tested against a live sensor, and continuously reshaped by the same loop. It has not been audited line-by-line by a human. The author makes no claim that flux is safe to run, produces useful intel, or won't do something surprising under load. Before deploying anywhere that matters, read
flux/server.pyend-to-end, run the test suite, and think hard about whatHONEYPOT_WEBSHELL_PATHS_CSVorFAKE_GIT_*hitting a real webroot would do. No warranties.
Each family is independently toggleable via env var; all default to
on (see CONFIG.md). Keyless deployments still 404
the canary-backed rows — dispatch requires TRACEBIT_API_KEY on top
of the master switch. We use Tracebit Community
for the canaries: free tier, sign up and drop the key in the env var.
| Trap | What it does | Key |
|---|---|---|
Fake /.env canary issuer |
Mints a per-request Tracebit Community canary and returns it as a .env-style payload |
yes |
Fake /.git/ repository |
Serves a loose-object git tree whose config/secrets.yml embeds a canary AND whose .git/config [remote "origin"] url embeds the same canary as HTTP Basic userinfo — so scrapers that only fetch .git/config (without cloning) still walk away with a live canary. Matches <prefix>/.git/* (apps deployed at subpaths) and is case-insensitive on the .git segment; ships a minimal-valid /.git/index (DIRC header) so git-dumper-style tools don't bail on a missing index. Per-IP cached so fan-out sees a consistent tree |
yes |
Fake /.svn/ working copy — docs |
Serves a Subversion working copy in both layouts Subversion has used: the pre-1.7 entries + text-base/ pair and the 1.7+ wc.db + pristine/ pair, where wc.db is a real SQLite database whose NODES checksums name pristine files the trap actually serves. Three independent canary placements — the repository URL as HTTPS Basic userinfo, Subversion's own auth/svn.simple credential cache, and the pristine .env — so which one a client takes measures how far into the working-copy format it got. Matches <prefix>/.svn/* and is case-insensitive on the .svn segment; per-IP cached so a dictionary sweep sees one consistent checkout |
yes |
| Canary file traps | Plausible file-format responses for /wp-config.php, /backup.sql, /id_rsa, /.aws/credentials, /api/v4/user, /users/sign_in, /actuator/env, /.vscode/sftp.json, GCP service-account JSON variants (/.config/gcloud/application_default_credentials.json, /firebase-adminsdk.json, the bare-filename dictionary /key.json, /keys.json, /keyfile.json, /gcp-sa.json, /token.json, /serviceAccountKey.json, /google-service-account.json, …), CI/CD config files, Terraform tfstate (/.terraform/terraform.tfstate, /terraform.tfstate(.backup) — docs), PaaS / .NET / IIS / Docker source-config (/Procfile, /heroku.yml, /app.json, /appsettings.json, /web.config, /auth.json, /Dockerfile — docs), niche cloud-provider CLI configs (OCI, Hetzner, Civo, Exoscale, Scaleway, Fly.io, OVH, OpenStack, Terraform Cloud, Pulumi, DigitalOcean, Linode, s3cmd, s3fs, Cargo, RubyGems, GitHub CLI, 1Password, Cloudflare Tunnel, WireGuard, Headscale — docs), … — full table below. Every entry also answers when it arrives nested under a recognised app-layout directory (/admin/aws.json, /config/phpinfo.php), capped at two segments and gated on a known-layout vocabulary so arbitrary parents still 404 — docs. The phpinfo family is gated on its leaf instead, so it answers under any parent — docs. Dependency manifests are answered for Node and, in each ecosystem's native format, for Composer, Bundler and pip — docs |
yes |
| Cloud instance / container role-credential service — docs | Serves both steps of the instance-metadata protocol (/latest/meta-data/…, plus the webroot-relative /aws/metadata/… and /.aws/metadata/… re-rootings) and the container credential-provider endpoints (/v2/credentials[/<id>], /ecs/task-credentials, /aws/ecs/task-credentials, /k8s/eks/credentials, …) alongside their non-secret task-metadata sibling (/v2/metadata, /v2/task). The listing steps return a role name with no secret and issue no canary; only a request that names a role — or hits a container endpoint — gets a credential envelope carrying a Tracebit AWS canary and a forward-computed Expiration. The two-step split is the point: a client that comes back for the role it was just told about is implementing the metadata protocol, not sweeping a dictionary |
yes |
| Shell rc files — docs | ~/.bashrc, ~/.profile, ~/.zshrc, ~/.bash_profile and siblings (plus /root/…, /home/ubuntu/… and app-layout prefixes) answered with an interactive-shell rc whose export AWS_* block carries a Tracebit AWS canary. Walked in the same sweep as the .env/.aws/credentials dictionary, and worth answering because an export line in an rc file is the standard workaround for a CLI that can't see the credentials file — so it holds credentials attached to the operator rather than the deployment. The home-dir spellings also make the file reachable through the /@fs/ read primitive |
yes |
| SSRF relay onto the metadata tree — docs | Answers the indirect route into the metadata tree above: a URL-taking parameter on a fetch-style entry path (/fetch, /proxy, /render, /preview, /read, /api/file, /load, /request, /webhook/test, …) pointed either at a link-local metadata address (AWS/Alibaba 169.254.169.254-layout, Azure's /metadata/… layout on the same address, or GCP metadata.google.internal) or at a local file — written either as a file:// URL or as the bare path spelling the same sweeps use (file=../../.env, path=/etc/passwd, backslash and double-encoded traversal included). Fires only when a parameter actually names a metadata host or a local file, so it never behaves like a working open proxy, and flux makes no outbound request and reads no real file on any branch. AWS targets resolve through the same path table and renderer as the direct trap, and file:// targets through the same shared file-read table every other read surface uses, so bodies are byte-identical and the credential steps carry the same Tracebit canary — while separate ssrf-relay-* log tags keep the populations apart. The Azure branch answers the managed-identity token endpoint, the one cloud where no long-lived credential file exists to steal, and records the resource audience the client asked a token for. The target URL is read from any parameter name and decoded twice, so the parameter dictionary the client is sweeping lands in the log as ssrfParam/ssrfTarget |
yes |
| Backup-archive canary — docs | Pattern-match <base>.<ext> backup-archive filenames against a ~95-name base dictionary × 17 compression extensions (tar.gz, zip, sql.gz, 7z, …), plus IP-octet- and date-derived filename synthesis (/65.20.84.180.tar.gz, /84.tar.gz, /2026.zip, /202603.tar.gz). Serves a real archive in the matching format containing .env + backup.sql with embedded Tracebit AWS canary. Catches generic backup hunters plus the per-target filename synthesis pattern from newer scanner tools |
yes |
| Webapp runtime-config bundle (JS + JSON) | Frontend SPA build-output env*.js / config*.js / *.json runtime-config files scanners harvest looking for builds where a developer committed populated cloud creds. Cross-product of an observed leaf-name set (env.js, config.js, app.js, main.js, index.js, env.production.js, config.production.js, config.local.js, runtime-config.js, env-config.js, environment.js, runtime.js, credentials.js, aws_creds.js, …; same names with .json: configuration.json, config.production.json, production.json, …) and an observed webroot-prefix set (/, /src/, /web/, /app/, /api/, /public/, /assets/, /static/, /dist/, /build/, /static/js/, /public/js/, /src/api/, /web/api/, /config/, …). JS form returns window.__APP_ENV__ = {...}; JSON form returns the same object as a parseable manifest. Both embed the canary in REACT_APP_AWS_* / VITE_AWS_* / NEXT_PUBLIC_AWS_* slots, with per-hit synthetic Sentry / Firebase / Stripe filler so the body looks like a real leaky build rather than a stub |
yes |
| SPA build manifest and the chunk it names — docs | Serves a Vite build manifest whose src/env-config.ts entry names assets/env-config-<hash>.js, where the hash is an HMAC of the client address under a per-process secret — not in any wordlist, not shared between deployments, not reachable by enumeration. The manifest holds no credential; the chunk it names holds the canary. Fetching that chunk is therefore evidence the client parsed the response rather than replaying a path list, which is the one thing every other referenced path here cannot show. A well-formed hash this process never issued is tagged separately as the shape a cross-deployment replay would take |
yes |
| PM2 process definition | Node process-manager descriptor. Its env_production block is injected into the process environment, which makes it the one file a Node deployment routinely keeps production credentials in and commits — canary goes in the AWS slots there. Also names script / cwd / log paths, so a client that parses rather than greps has a next step |
yes |
| AI-credential-file canaries | AI editor / coding-assistant configs (/.claude/{settings,settings.local,config,history.jsonl,.credentials,CLAUDE.md}, /.claude.json, /.config/anthropic/credentials/default.json, /.codex/{auth.json,config.toml}, /.gemini/{oauth_creds,settings}.json, /.cline/{settings,mcp_settings}.json, /.continue/config.json, /.cursor/mcp.json, /.cursor/User/globalStorage/state.vscdb, /.cursorrules, /.windsurfrules, /.clinerules, /.aider.conf.yml, /.sourcegraph/cody.json, /.config/open-interpreter/config.yaml, /AGENTS.md, …) plus per-vendor LLM API-key files (/.anthropic/api_key, /.dashscope/api_key, /.deepseek/config.json, /.kimi/, /.moonshot/settings.json) plus AI infrastructure / proxy configs (/litellm_config.yaml, /langsmith.env, /.huggingface/token, /.streamlit/secrets.toml, /baseten.yaml, generic MCP configs including root-dotfile /.mcp.json, /.bito/, /.codeium/, /.roost/, /cohere_config.json, …) and niche coding-agent tooling (/.openclaw/, /root/.config/opencode/, /root/.config/vastai/, /root/.nerve/, /root/.spawnrc, /root/.config/moltbook/) — listed in the same table; broken out in the footnote because Tracebit has no LLM canary type yet. Alongside these, generic cloud-storage credential stores (/rclone.conf, /.config/rclone/rclone.conf) get a real AWS canary in the S3-remote slot. |
yes |
| Fake webshell | Plausible File Manager on known *.php shell probe paths — a curated shell-name list, the /style.php × WP-directory family, and shell-jacking regex families (/.well-known/<name>.php, /.trash<N>/*, /.tmb/, /.dj/, /.alf/, …); simulates id / whoami / uname -a / cat /etc/passwd on follow-up commands — docs |
no |
| Shell-jacking sweep gate | Serves the same fake File Manager on any unclaimed *.php name in a shell-drop position — webroot, or a depth-bounded .php leaf under /wp-content/, /wp-admin/, /wp-includes/, where much of the hunting actually looks — but only to a source that has already asked for several distinct such names in one window, so a sweep gets caught while a one-off prober keeps the ordinary 404 and cannot use a random filename to prove the host fabricates responses. Records each source's dictionary width — docs |
no |
| Fake file-upload responder | Prefix-tolerant matchers for the legacy PHP file-upload libraries scanners walk (<prefix>/kcfinder/upload.php, <prefix>/jquery.filer/php/upload.php, <prefix>/jquery-file-upload/server/php/); GET returns presence-detection-friendly HTML/JSON/README, POST parses multipart parts and returns the per-family "uploaded OK" envelope. Logs fileUploadFamily / filenames / per-part content-types / fileUploadHasPhpShell so payload-bearing uploads are easy to triage. (CVE-2018-15706 / CVE-2018-9206 bait) — docs |
no |
| WordPress plugin upload-vector matrix | Answers the WordPress plugin arbitrary-file-upload endpoints tooling walks (wp-file-manager connector, contact-form-7-db, backup-backup, and the uploadFontIcon / wpr_addons_upload_file / ecsload admin-ajax.php actions), and keeps the landing path each one names consistent with the answer it gave. Exactly one vector per source is told its upload succeeded — chosen from a per-process secret, so the choice is stable for a source but is not the same vector on every host — and that vector's landing path serves the sender's own uploaded bytes back, while the refused vectors answer in their plugin's failure idiom and their landing paths stay 404 even against the sweep gate. One positive out of several tries is what a single writable plugin looks like; which vector the sender escalates on is the measurement — docs |
no |
| Code-execution and file-read API surface | Low-code-platform and web-IDE endpoints that take their argument in the request body (/api/fs/exec, <prefix>/lib/terminal-xhr.php, /api/v1/validate/code, /api/templates/preview, /api/designer/v1/file-content, /read-document). The argument is recovered from a JSON body, a form encoding or the query string under any of the key spellings these endpoints use, and logged before the response is chosen — a 404 is decided from the address alone, so for this family the payload was the one thing never recorded. The template endpoint answers the arithmetic a server-side-template-injection probe is made of ({{7*7}} → 49) with bounded integer operands and no evaluation of code; anything that is not that shape comes back unchanged — docs |
no |
| Modular tarpit + fingerprinting | Slow-drip response plus six fingerprinting modules (cookie, ETag, redirect chain, variable drip, Content-Length mismatch, DNS callback); fires on .env variants and on configurable first-contact paths (/, /index.html, /robots.txt, …) |
no |
| Fake LLM-API endpoint | Ollama / OpenAI / Anthropic-proxy JSON on /v1/models, /v1/chat/completions, /anthropic/v1/messages, /api/chat, … ; returns native SSE / NDJSON streaming when the client sets "stream": true so SDK-based callers read through to completion; logs model, prompt prefix, bearer / x-api-key token (sha256 + first-12 + last-4 preview) for cross-IP key-replay grouping — docs |
no |
| Fake AI-gateway proxy admin API — docs | The control plane beside the inference paths above: model registry (/model/info, /v1/model/info), key management (/key/info, /key/generate, both with and without the /v1/ prefix) and spend reporting (/global/spend/logs). A client walking these is after a gateway it can mint its own keys on, not after free inference. Any presented token is accepted and its sha256 + first-12/last-4 preview logged — the guessed master keys are the intel, and the hash groups the same key across IPs; a request with no token gets the proxy's own auth_error 401 rather than a 404, because that is the answer that makes the next request a key guess. The registry's managed-cloud model entry carries a per-request Tracebit AWS canary in the upstream aws_access_key_id / aws_secret_access_key slot — the operator's cloud credential rather than the proxy's, so a replay is visible at the provider. Every other credential-shaped field (other providers' keys, the minted virtual key, stored key hashes) is per-hit random. A key mint logs the requested models, budget and duration, i.e. the caller's intent before anything is spent; /key/generate on a non-POST returns the framework's own 405, which still confirms the route |
yes (/model/info only) |
| Apache mod_cgi traversal RCE (CVE-2021-41773 / CVE-2021-42013) — docs | Matches the encoded-traversal-to-interpreter shape (./%2e/%252e segments ending at bin/{sh,bash,dash,busybox}) on the raw request target, because normalisation collapses it to /bin/sh before any path matcher sees it. Reproduces the vulnerable-server asymmetry: a bodyless probe gets the 500 an unpatched 2.4.49 returns when the shell exits before emitting CGI headers (the positive vulnerability signal the scanner tests for), and the POST that follows gets simulated command output. Parses the echo Content-Type: …; echo; mod_cgi preamble off the body and logs cgiTraversalCommand plus the stage-2 cgiTraversalPayloadUrls / cgiTraversalDownloaders. File-read variants of the same CVEs are deliberately left to the canary trap table |
no |
| Fake MCP (Model Context Protocol) server endpoint | JSON-RPC 2.0 dispatch on /mcp, /mcp/, /mcp/messages, /api/mcp, /api/mcp/, /api/mcp/v1, /api/v1/mcp, /api/mcp/mcp + SSE handshake on /sse. A GET on the JSON-RPC endpoint carrying Accept: text/event-stream is the Streamable-HTTP server-to-client stream, so it returns the same event: endpoint handshake instead of a 405 that would end the walk before the payload-bearing POST; a plain GET still 405s. initialize / tools/list / resources/list / prompts/list / ping return a plausible catalog + capabilities envelope. tools/call on a secret-fetch tool name (fetch_secret, filesystem_read, list_workspace_files, database_query, read_env, …) mints a per-request Tracebit AWS canary and embeds it in result.content[0].text in .env shape; resources/read on env://AWS_* / file:///workspace/.env / file:///etc/mcp/credentials.json does the same in contents[0].text. shell_exec and other non-secret tools return isError: true with the attempted command captured to mcpToolArgsPreview for triage. Logs bearer / x-api-key token (sha256 + first-12 + last-4 preview) + mcpClientInfo.{name,version} for cross-IP MCP-scanner grouping — docs |
yes (tools/call + resources/read on secret targets only) |
| Agent / MCP service-discovery cards — docs | The /.well-known/ documents a client reads before it knows an endpoint exists. A2A agent card on /.well-known/agent-card.json (+ agent.json, agents.json), MCP server card on /.well-known/mcp[.json], /.well-known/mcp/server-card[.json], /.well-known/mcp/server.json and the webmcp[.json] spellings, and the OpenAI plugin manifest on /.well-known/ai-plugin.json (+ openai-plugin.json); %2e-encoded dot-segment spellings resolve too. Each card advertises a surface this same server already answers — the JSON-RPC endpoint for the agent and MCP cards, /openapi.json for the plugin manifest — and its tool names, resource URIs and transport URLs are read from the same catalogs tools/list and resources/list return, so the card and the endpoint cannot drift apart. No credential in any card body: the issuance is one hop on, at the tools/call the card exists to provoke. agentCardEndpoint logs the URL served, so a later mcp-server-* line from the same source against that endpoint is a client that read a discovery document and acted on it. Non-GET/HEAD gets the 405 a static document returns |
no (the endpoint it names issues) |
| Fake OpenAPI / Swagger spec | OpenAPI 3.0.3 JSON/YAML document on the SpringDoc / FastAPI / Swashbuckle / drf-yasg / NSwag canonical paths (/swagger.json, /v3/api-docs, /openapi.json, /openapi.yaml, /swagger/v1/swagger.json, /api-docs, /webjars/swagger-ui/index.html, …) plus Swagger UI / ReDoc bootstrap HTML; embeds the canary in securitySchemes.{bearer,apiKey}Auth.x-example, servers[].variables.adminApiKey.default, and the info.description text so credential scrapers that grab any of those slots get a replay-fireable key — docs |
yes |
| Fake GraphQL endpoint | GraphiQL HTML on GET /graphql (+ /api/graphql, /graphql/api, /api/gql, /v1/graphql, /query, /.well-known/graphql, …). POST body is classified: __schema / IntrospectionQuery returns a plausible schema listing User.apiToken / awsAccessKeyId / secretKey / refreshToken / webhookSecret fields. Follow-on data query against any credential-shaped field returns a per-hit Tracebit AWS canary in apiToken / accessToken / awsAccessKeyId / awsSecretAccessKey / secretKey slots (per-hit-random refreshToken and webhookSecret). Auth mutations (login, signIn, signUp, register, createUser, authenticate, resetPassword) return AuthPayload with the canary as token, capture the submitted username from inline literals or variables, and log graphqlHasPassword without storing the value. Unknown queries return a plausible Syntax Error / permission denied error envelope so the scanner keeps probing — docs |
yes |
| Fake SonicWall SSL VPN | SonicOS 7 SSL VPN portal HTML on /auth.html, /sonicui/7/sslvpn-portal/, /sonicui/7/login/, /cgi-bin/welcome and /cgi-bin/sslvpnclient (the portal form posts straight into the API chain below), plus JSON responses on the three paths in the CVE-2024-53704 auth-bypass chain; mints a per-request swap session cookie and logs submitted username, body sha + preview, and replayed session cookies — docs |
no |
| Fake Cisco WebVPN endpoint | Cisco SSL VPN landing page + launcher assets on /+CSCOE+/... and /+CSCOL+/...; also recognizes AnyConnect config-auth XML POSTs to / and logs submitted usernames without storing passwords — docs |
no |
| Fake Ivanti Connect Secure / Pulse Secure VPN | Ivanti SSL VPN welcome + login POST + HostChecker installer assets on /dana-na/... and /dana-cached/hc/..., including the bare /dana-na index and administrator-named sign-in realms (/dana-na/auth/url_<realm>/{welcome,login}.cgi, e.g. url_2) with the enumerated realm recorded as ivantiRealm; mints a per-request DSID cookie, logs username + has-password, and flips ivantiHasCmdInjection on shell-meta payloads aimed at /dana-ws/namedusers (CVE-2023-46805 / CVE-2024-21887 / CVE-2025-22457 chain bait) — docs |
no |
| Fake FortiGate SSL VPN | FortiOS SSL VPN login + /remote/logincheck credential POST, which lets a source find exactly one working credential after enough attempts and lands it on an SSL VPN portal (/remote/portal) with a per-request SVPNCOOKIE — so post-authentication behaviour becomes observable instead of every guess failing identically; plus /api/v2/cmdb/system/{admin,status,global} and /api/v2/monitor/router/policy REST stubs and the assets the login page and portal name for themselves; flips fortigateHasCmdInjection on shell-meta payloads aimed at the REST surface (CVE-2024-21762 / CVE-2023-27997 / CVE-2024-48887 bait) — docs |
no |
| Fake Palo Alto GlobalProtect | GlobalProtect portal + gateway prelogin XML on /global-protect/prelogin.esp and /ssl-vpn/prelogin.esp, login form + credential POST on /global-protect/login.esp (mints per-request PHPSESSID), gateway config on /global-protect/getconfig.esp; Server: PanWeb Server/ matches real appliance fingerprint (CVE-2024-3400 bait) — docs |
no |
| Fake Sophos XG SSL VPN | Sophos XG Firewall SSL VPN login portal on /svpn/index.cgi, /userportal/webpages/myaccount/login.jsp, /EndUserPortal.jsp and /userportal/Controller; credential POST captures username + has-password, mints per-request JSESSIONID cookie (CVE-2022-1040 bait) — docs |
no |
| Fake Barracuda SSL VPN | Barracuda Networks VPN tunnel negotiation on /myvpn (returns CONNECT + ipv4/ipv6 flags) and login portal on /cgi-mod/index.cgi (CVE-2023-7102 / CVE-2023-7101 reconnaissance surface) — docs |
no |
| Fake Check Point Mobile Access / Gaia | Mobile Access blade portal on /sslvpn/Login/Login, /Login/Login, /sslvpn/Portal/Main (credential POST captures username + realm + has-password, per-request CPCVPN_SESSION_ID cookie) and the Gaia management login on /cgi-bin/home.tcl, tagged apart so which surface a source came for is visible; plus the CVE-2024-24919 (CISA KEV) read primitive on /clients/MyCRL, whose body traversal resolves through the canary file table — the file a source chooses to read is logged, and a credential file hands back a live canary — docs |
no |
| Fake F5 BIG-IP APM / TMUI | BIG-IP Access Policy login on /my.policy (mints per-request MRHSession cookie), TMUI Configuration Utility on /tmui/login.jsp (prefix-matches /tmui/* to catch CVE-2020-5902 path-traversal), SSL VPN client negotiation on /sslvpnclient; flags f5HasPathTraversal on /../ indicators, Server: BigIP header (CVE-2023-46747 / CVE-2022-1388 bait) — docs |
no |
| Fake Docker Registry V2 API | Docker Distribution Registry HTTP API V2 surface: version check (/v2/), catalog listing (/v2/_catalog), tag enumeration (/v2/<name>/tags/list), manifest retrieval (/v2/<name>/manifests/<ref>), blob download (/v2/<name>/blobs/<digest>); logs Authorization headers, mutation attempts (PUT/PATCH/POST/DELETE), and repo/tag/digest per request. Multi-step protocol reveals scanner sophistication — docs |
no |
| Fake Docker Engine API (daemon on 2375) | Docker Engine API surface: /version, /info, /_ping, /containers/json, /images/json, plus the takeover chain POST /containers/create → POST /containers/<id>/start → POST /containers/<id>/exec → POST /exec/<id>/start → GET /containers/<id>/logs. The logs read-back is served as a correctly-framed Docker multiplexed stream (8-byte stdout/stderr header) and returns the stock 400 Bad parameters when the caller selects neither stream. /networks, /volumes and /system/df answer with stock local-daemon payloads; the swarm-manager routes (/swarm, /services, /secrets, /configs, /nodes, /tasks) return the real non-manager 503, staying consistent with the Swarm.LocalNodeState: inactive that /info advertises. POST /auth acks a registry login and records the submitted username, target registry and a SHA-256 of the password from either the JSON body or a base64 X-Registry-Auth header. Mints a fake 64-hex container/exec ID per create so the scanner ships its follow-up /start against the same trap. Strips a /vX.Y API-version prefix and a :2375 / %3a2375 / %253a2375 SSRF colon-port shim from the path before dispatching, and flags the SSRF shape as dockerDaemonHasSsrfPrefix. Parses the create body for Image / Cmd / Entrypoint and flips dockerDaemonHasPrivileged / dockerDaemonHasHostMount / dockerDaemonHasHostPid / dockerDaemonHasHostNetwork / dockerDaemonHasDangerousCap / dockerDaemonHasShellPayload for fast triage of cryptominer / host-takeover payloads — docs |
no |
| Fake Citrix NetScaler / Gateway portal | NetScaler ADC / Gateway login HTML on /vpn/index.html, the /logon/LogonPoint/ index, custom and tmindex pages, and /Citrix/XenApp/auth/login.aspx; credential POST sinks at /cgi/login and /p/u/doAuthentication.do mint a per-request NSC_AAAC session cookie (CVE-2023-4966 "CitrixBleed" leak shape — never a fixed literal). Flips citrixHasCmdInjection on shell-meta and CVE-2019-19781 path-traversal indicators (Shitrix); also covers CVE-2023-3519 / CVE-2022-27510 / CVE-2023-24486 fingerprint chains — docs |
no |
| Fake Microsoft RDWeb (RD Web Access) | RDWeb login page + /RDWeb/Pages/en-US/login.aspx credential POST (mints per-request TSWAAuthHttpOnlyCookie) plus /RDWeb/Pages/en-US/Default.aspx post-auth resource list. Landing set covers the short paths (/RDWeb, /RDWeb/, /RDWeb/Pages, /RDWeb/Pages/), every localized ASP.NET login/default page (/RDWeb/Pages/<xx-yy>/{login,Default}.aspx — matches any [a-z]{2}-[a-z]{2} locale so tr-TR / es-ES / zh-CN / fr-FR all route to the same handler), and the HTML5 Remote Desktop Web Client landings (/RDWeb/WebClient[/index.html]). After a "successful" landing-path POST (and on direct Default.aspx fetches on any locale), the resource list ships a single Cloud Console RemoteApp tile whose RDPFileContents HTML comment embeds a per-hit Tracebit AWS canary — credential-scrapers walking post-auth content harvest a key that fires on AWS replay. Logs DomainUserName / UserPass-presence so password-spraying credential rotations land in the access log; advertises Server: Microsoft-IIS/10.0 to match real Server 2019 RDWeb — docs |
no |
Fake ASP.NET Trace.axd disclosure |
/trace.axd (also /trace.axd/, case-insensitive, ignores ?id=<n> / ?clear=1 query) returns an ASP.NET Application Trace detail-view HTML page. The Tracebit AWS canary lands in three natural slots at once: Server Variables (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN), Headers Collection (Authorization: AWS4-HMAC-SHA256 Credential=<AKIA…>/… + X-Amz-Security-Token), and Session State (AwsProfile INI fragment) — a scraper grepping AKIA / AWS4-HMAC / AWS_ACCESS_KEY_ID all catch the same per-hit key. Per-hit Session Id / .ASPXAUTH / ASP.NET_SessionId / __VIEWSTATE / AWS4 request signature keep the body from acting as a fleet fingerprint; DbConnectionString uses _fake_db_password() — no fixed DB literal. Headers pin Server: Microsoft-IIS/10.0 + X-AspNet-Version: 4.0.30319 + X-Powered-By: ASP.NET — docs |
yes |
| Fake Microsoft Exchange (OWA / ECP / autodiscover / PSRemoting) | Multi-step Exchange surface: OWA login on /owa/auth/logon.aspx (mints per-request cadata cookie), ECP login on /ecp/ (mints msExchEcpCanary), eDiscovery exporttool ClickOnce manifest on /ecp/Current/exporttool/microsoft.exchange.ediscovery.exporttool.application (build version stamped in <assemblyIdentity> for fingerprint diff), autodiscover JSON on /autodiscover/autodiscover.json (per-request MailboxGuid + Bearer literal), and a 401 + WWW-Authenticate: Negotiate, Kerberos, NTLM on /powershell/. Flips exchange-autodiscover-proxyshell-ssrf on the literal ?@<spoof> query shape, logs exchangeXRpsCatPresent + exchangeHasPowershellCmdlet on PSRemoting POSTs, captures OWA / ECP usernames (username / j_username / UserName variants) without storing passwords. Headers pin Server: Microsoft-IIS/10.0 + X-OWA-Version (CVE-2021-34473 / CVE-2021-34523 / CVE-2021-31207 ProxyShell bait) — docs |
no |
| Fake IBM Aspera Faspex portal | Aspera Faspex login/logout/relay surfaces on /aspera/faspex/..., including the login form's own /aspera/faspex/session submit target (aspera-faspex-credential-post; accepts both the Rails-bracketed user[email]/user[password] pair the form renders and the generic spelling stuffing kits send); emits plausible HTML/JSON, logs follow-on payload previews on logout/relay endpoints, and keeps scanner chains alive past initial fingerprinting (CVE-2022-47986 bait) — docs |
no |
| Fake Hikvision IP camera | Hikvision ISAPI surface (/SDK/webLanguage, /ISAPI/Security/userCheck, /ISAPI/System/deviceInfo); returns plausible XML with Server: App-webs/ and a CVE-2021-36260-window firmware banner, flips hikvisionHasCmdInjection on shell-meta indicators in body/query so language-parameter command-injection payloads are easy to triage — docs |
no |
| Fake ONVIF device_service | ONVIF SOAP banner at /onvif/device_service (/onvif/services, /onvif/device, bare /device_service); returns a Dahua-class <GetDeviceInformationResponse> envelope with Server: lighttpd/ and a firmware version sitting in the public-disclosure window for the Dahua-class CVEs (CVE-2024-7029 auth-bypass cmd-injection, CVE-2023-43261, CVE-2021-33044). Logs onvifSoapActionHeader/onvifSoapActionBody and flips onvifHasCmdInjection on shell-meta or FirmwareUpgrade / UpgradeUrl sink-element indicators — docs |
no |
| Fake D-Link / Linksys HNAP1 router | SOAP-over-HTTP HNAP1 control endpoint at /HNAP1; GETs return a <DeviceSettings> envelope with vendor / model / Server: Mathopd/ banner, POSTs return a generic SOAP OK whose response element name tracks the SOAPAction header. Logs the raw SOAPAction value and flips hnap1HasCmdInjection on shell-meta indicators in the header / query / body so CVE-2015-2051 dropper payloads are easy to triage — docs |
no |
| Fake SOHO router / GPON-ONT web admin | Embedded-httpd admin surface behind Server: Boa/: BOA/Realtek /boaform/* forms, Netgear /setup.cgi, TP-Link /cgi-bin/luci/;stok=…, Tenda/ZTE /goform/*, Linksys /tmUnblock.cgi, and the generic login.cgi dictionary. Login paths serve a login page, or a success-shaped result plus a per-hit random SESSIONID once credentials arrive, so the exploit that normally follows a successful login still gets sent. Captures the submitted credential pair and lifts stage-2 dropper URLs out of injected commands into sohoRouterPayloadUrls / sohoRouterDownloaders (handles the +-encoded and scheme-less tftp -g -r forms) — docs |
no |
Fake Apache mod_status |
/server-status (/server-status/, ?auto, ?refresh=N); returns a plausible Apache 2.4 mod_status page (HTML or the ?auto text format) with a worker scoreboard whose recent-request URLs embed the per-hit Tracebit AWS canary in aws_access_key_id / aws_secret_access_key query-string slots — the slot credential-scrapers grep AKIA… from. Server: Apache/2.4.58 (Ubuntu) banner pinned inside the public-disclosure window for the 2024 mod_proxy / mod_rewrite CVEs so version-gated scanners don't bail — docs |
yes |
| Observability / debug surface — docs | The operational endpoints a framework exposes for monitoring: the Spring actuator discovery index (/actuator), Prometheus exposition (/metrics, /prometheus), Go expvar (/debug/vars), health probes (/health, /healthz, /readyz, …), Apache mod_info (/server-info), nginx stub_status, ELMAH (/elmah.axd) and the profiler index (/debug, /profiler, …). Every body names a further target — an internal database host, a config-file path, a link to another endpoint — so the follow-up request separates a client that parses a disclosure from one that merely collected a 200. The actuator index advertises exactly the endpoints flux answers — the canary-file table's set plus /actuator/{info,beans,loggers,auditevents,sessions,metrics} served from here (no credential slot, so no canary spend), and the templated /actuator/metrics/{name} — reconnecting a discovery chain that previously dead-ended on a 404 at the base path. info matters most: Spring Boot exposes health and info by default, so answering one and 404ing the other was a shape no real configuration produces, identically on every host. auditevents and sessions disclose account names, feeding the same enumerate-then-brute chain as the user-enumeration trap; session ids there are per-hit random. Issues no canary itself; the leads it names mint their own. Exact-path matching only, dispatched after every exact-path trap, so /server-status, /debug/pprof/ and /_profiler keep their closer-fitting handlers |
no |
| Fake GeoServer admin / OGC | GeoServer 2.x admin shell + About page + OGC *_Capabilities on /geoserver/..., plus the admin form's own /geoserver/j_spring_security_check submit target (geoserver-credential-post, logs username + password-length) and the Wicket stylesheet the shell references; flags OGNL/expression-language indicators in query string + body so CVE-2024-36401 payloads are easy to triage — docs |
no |
| Fake Liferay Portal JSON Web Services | Liferay 7.x JSON-WS discovery surface on /api/jsonws, /api/jsonws/, /api/jsonws?serviceClassName=..., and the /api/jsonws/invoke JSON-RPC sink. Landing HTML lists fake registered services and embeds the per-request Tracebit AWS canary in a DLAppService S3-backed Document Library config slot (the scanner-grep slot for portal-ext.properties leaks); per-service signature JSON embeds the same canary in getConfiguredS3Bucket.default.{accessKey,secretKey}. POST /invoke captures the JSON-RPC body and flips liferayHasMarshallerPayload on the canonical CVE-2020-7961 sink indicators (WrapperConnectionPoolDataSource / userOverridesAsString / ldap:// / rmi:// JNDI URLs / jodd.bean). Server: Apache-Coyote/1.1 + Liferay-Portal: headers pinned inside the CVE-2020-7961 public-disclosure window — docs |
yes |
| Fake Gravity SMTP plugin (WordPress REST) | Gravity SMTP plugin's WP-REST namespace at /wp-json/gravitysmtp/v1/settings, /v1/config, /v1/connector/{amazonses,mailgun,sendgrid,sparkpost,smtp,office365,gmail}, /v1/tests/mock-data, /v1/data/debug — plus WordPress install sub-directory placements (/{blog,wordpress,wp,site,news,cms,press}/wp-json/gravitysmtp/v1/...) that dispatch identically. /v1/config returns a populated per-connector config JSON; the AWS SES block embeds the per-request Tracebit AWS canary in aws_access_key_id / aws_secret_access_key (the same slot WP-REST authorisation-gap scrapers grep for after walking /wp-json/). Other connectors ship per-hit synthetic credentials in the published shape for each provider (key-<32hex> Mailgun, SG.<22>.<43> SendGrid, 40-hex SparkPost, ya29.… / GOCSPX-… Gmail OAuth) so the namespace looks realistic without leaking a fleet-wide fixed string. Anything else under /v1/... 404s with the WP-REST-shaped rest_no_route envelope. The whole namespace also answers via the ?rest_route= alias — docs |
yes (/config + /connector/amazonses only) |
| Fake Laravel Telescope debug panel | Laravel Telescope SPA + JSON-API surface at /telescope/<panel> (HTML Vue-app shell) and /telescope/telescope-api/<panel> (paginated entries JSON; /telescope/api/<panel> proxy-rewrite alias also dispatches). The requests panel ships a captured admin POST /admin/settings/s3 whose content.payload embeds the per-request Tracebit AWS canary in AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY (the slot a real RequestWatcher row stores); queries ships an insert into settings ... row with the canary in content.bindings; exceptions ships an Illuminate\Database\QueryException whose content.context.env carries the canary in the $_ENV dump (same slot Ignition leaks via HTML); mail ships a PasswordResetMail with the canary in content.transport.{key,secret} for the SES driver; logs ships an error-level entry with the canary in content.context.AWS_*. Other known panels (cache, redis, gates, dumps, schedule, jobs, batches, views, models, events, commands, notifications, monitored-tags, clients) return an empty {"entries":[]} — the empty state a fresh install renders. Bearer tokens, CSRF, session cookies, APP_KEY, DB_PASSWORD, REDIS_PASSWORD, and the captured /api/v1/login password are all per-hit secrets.token_urlsafe/token_hex synthetics — no fixed literals. Sub-directory placements (/admin/telescope/..., /dashboard/telescope/..., /panel/..., /backend/..., /app/..., /laravel/..., /monitor/..., /dev/..., /internal/...) dispatch identically to the bare path — docs |
yes (requests / queries / exceptions / mail / logs panels only) |
| Fake ColdFusion admin / component browser | ColdFusion public .cfm anchors plus /CFIDE/componentutils/, Administrator, and AdminAPI surfaces; logs method, auth/session hints, and exploit payload indicators — docs |
no |
| Fake Atlassian Confluence + Apache Struts OGNL | Confluence 7.x login surface on pages/createpage-entervariables.action, pages/doenterpagevariables.action, templates/editor-preload-container, users/user-dark-features, login.action / index.action and the login form's own dologin.action submit target — which captures the submitted username + password-length and whether the per-render atl_token CSRF nonce was echoed back (confluence-credential-post) — all under the bare, /confluence/ and /wiki/ prefixes; also matches URL-encoded ${@...} OGNL in the path (CVE-2022-26134) and ${#...ProcessBuilder...} / redirect:${...} / redirectAction: / xwork.MethodAccessor / _memberAccess Struts S2-053/S2-061/S2-066 OGNL carried in the query or body; extracts the OAST/Interactsh callback hostname and preserves the full payload preview so the embedded sh -c <cmd> is recoverable for triage — docs |
no |
| Fake OIDC / OAuth discovery endpoint | Keycloak-shaped OpenID Connect / OAuth 2.0 Authorization Server metadata JSON on every prefix scanners walk for IdP discovery: /.well-known/openid-configuration (bare, /oauth/, /oauth2/, /oauth/idp/, /idp/, /auth/, /auth/realms/<realm>/, /realms/<realm>/), the /.well-known/oauth-authorization-server RFC-8414 sibling, the openid_configuration underscore-typo variant, leading-slash URL-encoding (/%2F.well-known/... / /%252F.well-known/...) and %00 / .txt / ~ / ?v=1 suffix noise. Keycloak realm name extracted from the path and reflected in the issuer + endpoints; embeds the per-request Tracebit AWS canary in non-standard _aws_metadata_signing_* extension fields so credential-harvester grep loops scraping discovery JSON for AKIA… walk away with a replay-fireable canary; OAuth-sibling responses drop OIDC-only fields (userinfo_endpoint, id_token_*, claims_supported) per RFC-8414 — docs |
yes |
| OIDC endpoints — the addresses the discovery document advertises — docs | Serves every endpoint the discovery document publishes, which all returned 404 until now: jwks_uri (per-hit-random JWKS, plus the /.well-known/jwks.json / /jwks.json / /oauth2/certs spellings guessed without reading the document), authorization_endpoint (Keycloak login form for a client id the product ships, the Invalid parameter: client_id page for any other), the login form's own login-actions/authenticate submit target (captures username + password digest), and the credential-bearing token / token/introspect / revoke / userinfo / clients-registrations endpoints, each answering the invalid_client / invalid_token envelope a real unauthenticated caller gets. Captures the caller's own redirect_uri / redirect_uris callback infrastructure; credential-shaped fields are logged as sha256 + length, never in clear. post_logout_redirect_uri is logged and never honoured, so the endpoint cannot become an open redirector. A shared endpoint table backs both the advertised URLs and the matcher, so an advertised route cannot drift away from a served one |
no |
| Fake phpMyAdmin login page | phpMyAdmin 5.x cookie-auth login page across the classic install-path aliases (/phpmyadmin/, /phpMyAdmin/, /PMA/, /myadmin/, /dbadmin/, /mysql/, /admin/phpmyadmin/, hyphenated /php-my-admin/, /php-myadmin/, /mysql-admin/, …) plus per-version directory variants and the deep paths under them (/phpmyadmin4.8.1/, /phpMyAdmin-2/index.php, /phpmyadmin2/sql.php, /PMA2018/setup/). GET / HEAD renders the canonical PMA login HTML with a per-request hidden token + per-request phpMyAdmin=<session> cookie; POST captures pma_username, pma_password length (never the password itself), server selector, and submitted token, then re-serves the login HTML with the standard Cannot log in to the MySQL server error notice and the submitted username echoed back the way real PMA does. /setup/ probes get their own phpmyadmin-setup-probe result tag so the setup-page fanout is separable — docs |
no |
| Fake Adminer login page | Adminer 4.x login page across the classic install-path aliases (/adminer.php, /adminer/adminer.php, /adminer/index.php, /adminer/, /admin/adminer.php, /db/adminer.php, /database/adminer.php, /mysql/adminer.php, /tools/adminer.php, /backup/adminer.php, /wp-content/plugins/adminer/adminer.php, per-version /adminer-4.8.1.php, …). GET / HEAD renders the canonical Adminer login HTML with a per-request hidden token + per-request adminer_sid_<slot>=<session> cookie; POST captures the auth[username] / auth[server] / auth[db] / auth[driver] / auth[permanent] fields plus auth[password] length (never the password itself), re-serves the login HTML with the Invalid credentials. error notice, and embeds the per-request Tracebit AWS canary in a <datalist> server-history preset entry (s3-backup.internal.example.com:3306 with data-aws-key / data-aws-secret slots — a scanner walking the post-login page for AKIA… harvests a replay-fireable key). Bare-GET banner-grab probes never see the canary; only credential-post capture triggers the mint — docs |
yes (on POST only) |
| Fake cPanel / WHM login page | cPanel WebHost Manager 11.x login page across the canonical entry aliases (/whm, /whm/, /2086/, /2086/login/, /2087/, /cpanel, /2082/, /2083/, /session/login, …), the cpsrvd proxy-subdomain entry points used when a host is reached via its whm. / cpanel. / webmail. service subdomain rather than by port (/___proxy_subdomain_whm/login/, /___proxy_subdomain_cpanel, …) plus the /openid_connect/cpanelid handoff, and the post-redirect cpsess-tokenised shape (/cpsess<16-64 hex>/whm/, /cpsess<hex>/session/login/, /cpsess<hex>/cpanel/<subpath>). GET / HEAD renders the WHM login HTML with a per-request cpsess<hex> token embedded in the form action + asset URLs and a per-request cprelogin=<hex> cookie; when the scanner hits a /cpsess<hex>/… alias directly, the same token is preserved so a follow-on POST keeps session context. POST captures user + pass length (never the password itself) plus the hidden goto_uri / goto_app redirect targets (whostmgrd separates WHM-root-targeting from cPanel-user-targeting cpaneld on the same form), re-serves the login HTML with the standard The login is invalid. error notice and the submitted username echoed back. Server: cpsrvd/11.126.0.5 banner matches real WHM — docs |
no |
| Fake 3CX web client API | 3CX software-PBX browser client shell on /webclient/ plus its JSON auth API. POST to /webclient/api/Login/GetAccessToken (and /Login, /LoginUser, /GetTokenAuth, /RefreshToken) returns an OAuth-shaped token envelope whose access_token / refresh_token are minted per hit, and captures the submitted username, password length, second-factor code and remember-me flag; the endpoint speaks JSON, so a form-body parser reads nothing off it and threecxBodyEncoding records which encoding arrived. Bare GET returns 405 Allow: POST like the real API. A post-authentication surface (/webclient/api/Users/current, /Settings, /MyDevices, /Recordings, /Voicemails, …) stays open so a client that accepts the envelope has somewhere to go, with threecxBearerPresent recording whether it carried the token it was just issued. The bare /api/login sibling is deliberately left to the generic web-app form trap — docs |
no |
.DS_Store filesystem-metadata leak trap |
/.DS_Store (and /.DS_Store/, case-insensitive) returns a plausibly-shaped Finder .DS_Store binary (\x00\x00\x00\x01Bud1 header + DSDB master record + one Iloc record per entry), served as application/octet-stream with a per-request ETag. Records embed UTF-16BE filenames that a scraper's strings -e b (or a mac_apt-style .DS_Store parser) extracts as a leaked directory listing; every default entry name points at a path flux already handles (.env, .env.production, .git, .aws, backup.zip, backup.sql, admin, phpmyadmin, wp-admin, wp-config.php.bak, config, adminer.php, deploy), so the follow-up crawler routes back into other traps. Success signal is the follow-on probe volume from the same source, not the single .DS_Store fetch — docs |
no |
| Fake Oracle WebLogic Admin Console | /console, /console/, /console/login/LoginForm.jsp, /console/j_security_check (case-insensitive) serve the Oracle WebLogic Server AdminServer login flow: Oracle-branded HTML with WebLogic-Version <meta> tag, form posting to /console/j_security_check with J2EE FORM-auth field names (j_username / j_password), per-request ADMINCONSOLESESSION=<hex>; Path=/console; HttpOnly cookie. POST captures the submitted username + password-length, re-serves the login page with the Authentication Denied error notice, mints a fresh session cookie. Advertises Server: WebLogic Server 14.1.1.0.0 + Oracle Diagnostic-Message-Service context IDs (X-ORACLE-DMS-ECID / X-ORACLE-DMS-RID) so scanners confirming a WebLogic origin see the exact banner shape. CVE-2020-14882 / CVE-2020-14750 / CVE-2019-2725 / CVE-2019-2729 / CVE-2023-21839 reconnaissance surface — docs |
no |
| Fake SAP NetWeaver Visual Composer MetadataUploader | NetWeaver Visual Composer servlet at /developmentserver/metadatauploader (plus /irj/, /nwa/, /sap/ webroot prefixes); GET returns the SAP-formatted XML error envelope, POST parses the multipart body, logs filename / content-type / sapMetadataUploaderHasJspShell / sapMetadataUploaderHasXxe, and returns the plaintext "OK: stored …" receipt with the uploaded filename echoed back (CVE-2025-31324 unauth file-upload + CVE-2017-9844 XXE bait) — docs |
no |
Fake Drupal /user/register + settings.php |
Drupalgeddon2 trigger surface at /user/register (also /?q=user/register, /drupal/, /cms/ prefixes); GET returns a Drupal 8/9 user-registration form with per-request form_build_id / form_token, POST captures the mail[#post_render] / element_parents chain and flags drupalHasDrupalgeddon2 / drupalHasRcePayload for triage. Twin canary at /sites/default/settings.php (+ .bak / .swp / ~ / %00 / %20 / default.settings.php / /sites/all/ / /drupal/ / /cms/ variants) ships a fully shaped Drupal config with per-hit synthetic DB password + Tracebit AWS canary in the s3fs.settings block (CVE-2018-7600 bait) — docs |
yes (settings.php only) |
| Fake Joomla 4 public-config disclosure | Joomla 4 WebService config-disclosure endpoint at /api/index.php/v1/config/application plus per-component variants /api/index.php/v1/config/com_users etc.; ?public=true returns the full configuration.php-shaped JSON envelope with per-hit synthetic DB / SMTP password + site secret and the Tracebit AWS canary in the filesystem.s3.access_key / secret_key / session_token slots (the documented Joomla 4 S3 storage-driver leak), the auth-denied branch returns a Joomla-shape {"errors":[...401...]} envelope so the scanner keeps walking. X-Powered-By: PHP/8.1.27 (CVE-2023-23752 bait) — docs |
yes |
Tomcat /..;/env.* path-normalization bypass |
Catches the path-parameter-bypass shape /..;/env.js (+ env.dev.js / env.prod.js / env.production.js / env.development.js) used by evasion-aware scanners chaining a Tomcat-style proxy bypass with a frontend-bundle env-file harvest. Returns a plausible runtime-config JS bundle (window.__APP_ENV__ = {...}) with the Tracebit AWS canary in the REACT_APP_AWS_* / VITE_AWS_* / NEXT_PUBLIC_AWS_* slots scrapers grep on, plus per-hit synthetic Sentry DSN / Firebase app id / Stripe publishable key. tomcatHasPathNormBypass=true is logged on every hit so the evasion-aware-actor population is separable from regular env-file canary harvest at one log field. /tmui/-prefixed bypass shapes continue to route to the F5 handler — docs |
yes |
| Fake Spring Cloud Gateway Actuator extension | /actuator/gateway/routes, /actuator/gateway/routes/{id}, /actuator/gateway/refresh, /actuator/gateway/globalfilters, /actuator/gateway/routefilters, /actuator/gateway/routepredicates (plus /manage/gateway/, /management/gateway/, /api/actuator/gateway/ reverse-proxy aliases); GET returns a fake route list with the Tracebit AWS canary embedded in metadata.adminApiKey + metadata.adminApiSecret and inside an AddRequestHeader filter named X-Admin-Api-Key. POST to a route id captures the SpEL body, flips springGatewayHasSpel on #{T(…) / T(java.lang.*) / getRuntime / ProcessBuilder indicators, and returns 201 Created. /refresh returns 200 (matches real CVE-2022-22947 chain step); Server: Spring Cloud Gateway/3.1.0 banner pinned inside the public-disclosure window — docs |
yes |
| Jolokia JMX-over-HTTP operations — docs | The follow-ups the MBean listing invites. read/<mbean>/<attribute>, exec/<mbean>/<operation>/<args…>, write, search, list/<domain> and version under every mount the listing is served at (/actuator/jolokia, /jolokia, /api/jolokia, /manage/, /management/, /monitoring/, /app/, /backend/ spellings), plus the POST bulk-request form. Listing and operations answer from one MBean tree, so a bean that is advertised is a bean that reads. The AWS canary rides the -Daws.accessKeyId / -Daws.secretKey launch flags surfaced by java.lang:type=Runtime and by the diagnostic bean's vmCommandLine / vmSystemProperties — the operations the endpoint is swept for. Unknown beans get the agent's own InstanceNotFoundException envelope inside a 200, so the client keeps naming beans and the dictionary keeps arriving. jolokiaHouseBean separates a client that walked our tree from one running the stock agent fingerprint; version / search / delegate reads mint nothing |
yes |
| Fake Next.js + SSJS-injection responder | Empty page-data JSON on /_next/data/<buildId>/*.json and Next.js-conventional /api/* routes; on a ?cmd=<base64> payload that decodes to a JS-eval shape (require('child_process').execSync(cmd)), extracts the inner var cmd = "echo X" literal and reflects the simulated output back, falling back to the scanner's own ERROR catch-block sentinel for unrecognised commands. Also covers the dev-mode internal endpoints (/__nextjs_action, /__nextjs_launch-editor, /__nextjs_error_overlay, /__nextjs_original-stack-frame, /__nextjs_stack_frame) with shape-appropriate responses: empty RSC for Server Actions (POST body captured), {"opened":true} for the IDE-launch endpoint (with file=/line= query args captured), and a per-hit HTML stack frame for the overlay endpoints. URL-encoded leading-slash bypasses (/%2f__nextjs_action, /%252f__nextjs_action%2f) normalise back to the same dispatch — docs |
no |
| Cmd-injection / printenv responder | /admin/config?cmd=… and /admin/config.php?cmd=… (admin-shell exploit shape) plus /printenv, /cgi-bin/printenv, /cgi-bin/test-cgi; classifies the cmd value, returns a plausible cat /etc/passwd / id / uname body, and mints a per-request Tracebit AWS canary when the cmd asks for ~/.aws/credentials or env vars — docs |
yes |
| PHP/body-RCE responders | Body-driven exploit responders for PHPUnit eval-stdin.php, PHP-CGI auto_prepend_file=php://input (CVE-2024-4577), and Apache CGI path-traversal /bin/sh; logs request-body payloads and decoded base64 command hints. Bare GET/HEAD against /cgi-bin/php, /cgi-bin/php-cgi, /cgi-bin/php{5,7,8}{,-cgi,.cgi}, /cgi-bin/, and /cgi-bin returns the canonical <br />\n<b>No input file specified.</b> 200 page with Server: Apache/2.4.41 (Win64) … PHP/7.4.33 + X-Powered-By: PHP/7.4.33 so the gated CVE-2024-4577 scanner population (the one that liveness-probes before sending the exploit) follows up with the exploit POST, which then lands in the body-RCE handler above — docs |
no |
| Web-app form responder | Plausible HTML forms on /login, /signin, /signup, /register, /checkout, /cart, /contact, /subscribe, /newsletter, /dashboard, /profile, /settings, /admin, /auth/*, /api/{login,signup,contact,…}; GET returns the form with a per-request hidden CSRF token, POST returns a 302 back to the form (auth-failure shape) with a per-request session_id cookie. Logs extracted username/email, has-password/email flags, the submitted field-name list, and the body preview/sha256 so credential-stuffing payload rotations land in the access log — docs |
no |
| Framework dev-mode debug surfaces | Symfony Web Profiler phpinfo (/_profiler/phpinfo, /app_dev.php/_profiler/phpinfo, /symfony/_profiler/phpinfo, /frontend_dev.php/_profiler/phpinfo) plus profiler dashboard (/_profiler/latest, /_profiler/search, /_profiler/ and the bare /_profiler, with the same /app_dev.php/, /symfony/, /frontend_dev.php/ prefixes) returns a phpinfo-shaped page with $_ENV carrying the AWS canary + per-hit APP_SECRET / DATABASE_URL / MAILER_DSN; Symfony parameters.yml (/parameters.yml, /config/parameters.yml, /app/config/parameters.yml) and the dev-mode /_profiler/open local-file-read endpoint return a YAML body with the canary in aws_* keys plus per-hit database_password / mailer_password / secret; Yii2 debug toolbar (/debug/default/view, /web/debug/default/view, /frontend/web/debug/default/view, /backend/web/debug/default/view, /sapi/debug/default/view, /debug/default/db-explain) returns an HTML page mimicking yii\debug\Module ConfigPanel with $_ENV and components.db.* / components.mailer.* carrying the same canary set; Django debug toolbar (/__debug__/render_panel/, /__debug__/sql_select/, /__debug__/sql_explain/, /__debug__/sql_profile/, /__debug__/template_source/) returns SECRET_KEY + DATABASE_URL + AWS canary in a SettingsPanel-shaped page; Laravel facade/ignition error page (/_ignition/execute-solution plus /api/_ignition/... / /backend/_ignition/... reverse-proxy aliases, /_ignition/health-check, /_ignition/scripts/ignition.js, /_ignition/styles/ignition.css) returns an Ignition stack-trace HTML page whose "Environment" panel carries APP_KEY / DB_PASSWORD / REDIS_PASSWORD / MAIL_PASSWORD (per-hit) + the AWS canary triple; CVE-2021-3129 POST exploit bodies captured via bodySha256 — docs |
yes |
| Fake Laravel Debugbar stored-request browser | Debugbar's stored-request store at /_debugbar/open — the surface a Laravel app exposes when APP_DEBUG=true reaches production or the package sits in require rather than require-dev. A two-step protocol: op=list (and the bare /_debugbar) returns request metadata only — ids, method, uri, timestamp, no secret — while op=get&id=<id> returns one captured request in full, with the AWS canary in the request.env $_ENV dump and repeated in the queries panel's statement bindings. Stored-request ids are derived deterministically per host, so debugbarIdKnown records whether the client read our listing or guessed — the same discriminator the metadata tree gets from imdsRole. APP_KEY / DB_PASSWORD / REDIS_PASSWORD / MAIL_PASSWORD / CSRF token / session id are per-hit synthetics; assets (/_debugbar/assets/{javascript,stylesheets}) and the Clockwork shim answer without spending a canary — docs |
yes (op=get only) |
| Fake Auth.js / NextAuth credential-provider surface | The /api/auth/* route table a Next.js app exposes (providers, csrf, session, signin, callback/<provider>, signout, error), served under both the default prefix and the /auth basePath override. The credentials sign-in is a two-step CSRF exchange, so the per-source token doubles as a client probe: nextauthCsrfKnown separates a tool that fetched the token and echoed it from one blind-POSTing a path list. Reads both form and JSON bodies, captures nextauthUsername (never the password) and the attacker-supplied nextauthCallbackUrl — an open-redirect target, so an off-host value names their own infrastructure. Every attempt is rejected with the framework's own error=CredentialsSignin redirect; no session is ever granted — docs |
no |
| Fake WordPress wp-login.php canary | WordPress 6.x login page on /wp-login.php with per-hit _wpnonce hidden field + wordpress_test_cookie; POST captures log/pwd and checks whether the submitted nonce matches one previously issued to the same IP (nonce-harvesting vs blind-POST attribution); /wp-admin/* paths redirect to the login page. Logs wpLoginUsername, wpLoginNonceMatch, wpLoginTestcookiePresent — docs |
no |
| Fake WordPress setup-config.php install wizard | WordPress pre-install wizard on /wp-admin/setup-config.php and the multi-install subdirectory forms (/wordpress/, /blog/, /wp/, /cms/, /site/, /shop/, /portal/, …). Walks the real three-step flow: welcome page → database connection form → the genuine "All right, sparky!" successful-connection page that routes on to install.php. The capture is inverted from the other login traps — the POST records the database host, username and password the client wants this install to dial, i.e. infrastructure the client controls rather than a guess at ours, which is why the password is retained in full here. wpSetupConfigDbHostIsRemote flags any non-loopback endpoint (an untouched form submission leaves localhost in place); wpSetupConfigPrefixIsDefault separates off-the-shelf scripts from hand-driven ones. Dispatched ahead of the /wp-admin/* redirect so the wizard stays reachable — docs |
no |
| Fake WordPress REST API index | The discovery document at /wp-json/ (bare /wp-json, install subdirectories, and the ?rest_route=/ query form permalink-less installs use) plus every route it advertises. WordPress-shaped index: site identity derived from the request Host, registered namespaces, and a routes map whose self links are the addresses a client follows next. Collections (posts, pages, media, categories, tags, comments) return listings with the X-WP-Total / X-WP-TotalPages headers a real install emits, indexed items return the object or core's rest_post_invalid_id / rest_term_invalid 404 envelope, descriptor routes (types, taxonomies, statuses) return their object maps, and the auth-gated routes (settings, plugins, themes, users/me) answer 401 with core's own error code — the response that proves the route is registered. Routes WordPress does not register (/wp/v2/custom-css, /wp/v2/attachments) and the prefix-less /wp/v2/... spelling stay 404, because a real install 404s them and answering would be a fleet-wide tell. Issues no canary; it is the chain that reaches the ones that do — users and batch/v1 keep their own handlers, dispatched ahead of this one. A guard test walks every advertised self link over the real dispatch path, so the index cannot drift into advertising a dead route — docs |
no |
| Fake WordPress user-enumeration trap | WordPress core REST /wp-json/wp/v2/users[?per_page=…] and /wp-json/wp/v2/users/<id> return a JSON list/object of fake author slugs; WordPress 5.5+ core sitemap shards /wp-sitemap-users-N.xml and Yoast/RankMath /author-sitemap[N].xml return XML sitemaps with the same fake /author/<slug>/ URLs. Unknown REST ids serve a rest_user_invalid_id 404 envelope. Logs wp-user-enum-rest-list / wp-user-enum-rest-single / wp-user-enum-core-sitemap / wp-user-enum-yoast-sitemap with wpUserEnumVariant. The REST surfaces also answer via the ?rest_route= alias — docs |
no |
| Fake WordPress REST Batch API | WordPress core's request multiplexer at /wp-json/batch/v1 (5.6+) — the endpoint credential tooling uses to amplify a brute-force run past per-request rate limits. POST returns 207 with a per-sub-request responses array; the sub-request array itself is logged first (wpBatchRoutes, wpBatchMethods, wpBatchValidation, plus wpBatchUsernames and per-attempt password hashes when bodies carry credentials), so the operator's whole intended action list is captured in one row before any of it is attempted. Sub-requests enumerating users get the same fake author roster the user-enum trap serves, feeding the follow-up brute into the wp-login capture; anything else gets the stock rest_no_route envelope. GET gets the POST-only route's rest_no_route; malformed, requests-less and over-cap (25) bodies get core's matching 400 envelopes. Install subdirectories and the ?rest_route= alias both dispatch — docs |
no |
| WordPress REST route alias (normalisation, not a trap) | Resolves ?rest_route=/<namespace>/<route> to its canonical /wp-json/... form ahead of every path matcher, so the WP-REST traps are reachable at both addresses WordPress serves them from rather than one. Decodes %2F separators, first-wins on a repeated parameter, and fires only on a WordPress front controller (/ or /index.php, root install or common subdirectory). Records wpRestRouteAliasFrom + wpRestRoutePath beside the request as it arrived. The prefix-less spelling of the same routes (/wp/v2/posts/<id>, no base segment) is deliberately not rewritten — a real install serves REST under exactly one rest_url_prefix, so answering both spellings would be a fleet-wide tell — but it is named with wpRestPrefixlessRoute, which makes the injection-testing runs that use it countable without changing a response byte — docs |
no |
| Fake Vite dev-server env-leak | /@vite/env returns a Vite client-module body whose context.define flat-key block exposes VITE_* env vars — VITE_AWS_ACCESS_KEY_ID / VITE_AWS_SECRET_ACCESS_KEY / VITE_AWS_SESSION_TOKEN / VITE_API_KEY carry the per-hit Tracebit AWS canary, so frontend-env scanners that grep raw bytes for VITE_ / AKIA patterns harvest a replay-fireable key. Per-hit-unique VITE_SENTRY_DSN public key / VITE_S3_BUCKET suffix / VITE_APP_ID keep the body from acting as a fleet fingerprint. The /@fs/ FS-walk pivot scanners chain after this probe is handled by the Vite /@fs/ arbitrary-file-read trap below — docs |
yes |
Vite /@fs/ arbitrary file read |
Resolves /@fs/<absolute path> — Vite's dev-server filesystem passthrough, an unauthenticated arbitrary-read primitive under the disclosed server.fs.deny bypasses — onto whichever existing trap serves the named file, by walking leading directory segments off the path until a suffix matches. An absolute prefix we have never catalogued (/usr/src/app/.env, /home/node/.aws/credentials) therefore still lands on the right renderer and its canary, and every trap added later is reachable through /@fs/ for free. A fixed list of system files (/etc/passwd, /etc/nginx/nginx.conf, /etc/shadow, php.ini at every packaged location, and the kubelet's projected service-account volume — token, namespace, ca.crt under both the /var/run and /run spellings) is answered statically after the credential walk misses, so the oracle a scanner reads to confirm the primitive works no longer 404s while the credential files it gates succeed. The service-account token is a per-hit JWT, so a read that leaks one stays attributable. Unresolvable paths 404 like a real dev server; the requested path is logged either way, which is the only place flux records an attacker's assumed on-disk layout — docs |
yes |
php://filter local file read |
Answers any query carrying a php://filter stream-wrapper URL — the arbitrary-read primitive that arrives in a parameter rather than in the request target. The named resource resolves through the same suffix walk the /@fs/ primitive uses, so every file already furnished for that surface (and every trap added later) is readable here for free, canary included. The response is then run through the filter chain the client asked for — a client that requested convert.base64-encode and got plaintext back would have a body that cannot have come from the filter it named, so it would learn the sink is fake and discard the credential instead of replaying it. Also serves distro php.ini at every packaged location, since a read reached through a PHP include bug is read by PHP and that file is what says whether the next step is possible. phpFilterParam separates the three populations that share this syntax: include-sink discovery sweeping parameter names, a named-advisory read, and PHP-CGI argument injection. Misses return an empty 200, not a 404 — a missing include emits nothing and the script still returns — docs |
yes |
| Canary-echo observer — docs | Not a route: an annotation on whatever line the request was already going to produce. Notices an AWS access key id (AKIA/ASIA + 16) anywhere in the request target, any request header, or the head of the body, and classifies it own (this process served that exact key), account (same issuing account, served by a sibling host — the only evidence available that a harvest is being replayed away from where it was taken) or foreign (somebody else's loot). Logs canaryEchoKeyIds / canaryEchoMatch / canaryEchoIn / canaryEchoCount. The response never changes — a server that answered its own canaries differently could be separated from a real one by sending it a key and watching what changed. Recognition is learned at runtime through _aws(), the single point every renderer reads a canary through, so nothing about the issuing account is committed to this public repo. The key id is logged; the matching secret is neither matched on nor copied into any field |
no |
| Fake Tomcat Manager — docs | The Manager and Host Manager applications, plus the JBoss consoles the same dictionaries walk beside them (/jmx-console, /web-console, /admin-console, /invoker/JMXInvokerServlet). Unauthenticated requests get 401 with WWW-Authenticate: Basic realm="Tomcat Manager Application" and Tomcat's own 401 page rather than a 404 — which is the whole point, since a 404 ends the exchange and a 401 starts a brute. Any credential is then accepted, because a sink that can never succeed records the dictionary and nothing about what a client does once it is in: the username, the password's sha256 and its length are logged (never the value), and the served page carries the real deploy form so a parsing client has a concrete next request. Uploads to the deploy addresses are captured by size, sha256 and zip-header check. /manager/text/* answers the OK - … format tooling parses; /manager/jmxproxy serves the Runtime bean's environment with a per-request Tracebit AWS canary in the AWS_* slot, which is where a deployment's cloud keys really live and why that bean gets asked for. Bare /status and /debug are deliberately not claimed. Gated on the issuing key as a whole, so a keyless deployment 404s every surface rather than splitting |
yes (/manager/jmxproxy only) |
| Interpolation-payload observer — docs | Not a route: a second annotation on whatever line the request was already going to produce. Notices an expression the server was expected to evaluate — ${jndi:…} in any scheme, Log4j ${env:…} / ${sys:…} lookups, OGNL %{…}, SpEL ${T(…)} — in the request target, any header, or the head of the body. Collapses the per-character evasion spellings (${::-j}, ${lower:j}) first, so obfuscated payloads match the same pattern as plain ones. Splits the two halves that matter: interpolationCallbacks is the scheme://host a lookup points at (the sender's own infrastructure, so it identifies the sender rather than the tool), and interpolationLookupKeys is the env:/sys: variable names asked for (a statement of what they collect, made before they have collected anything) — a lookup nested in the host position is the exfiltration channel, not a hostname, and is never filed as one. Also logs interpolationFamilies / interpolationIn / interpolationCount / interpolationSamples. The response never changes, for the same reason as the echo observer above. These requests carry no path worth matching, so without this they are indistinguishable from any other 404 |
no |
| Inbound webhook receiver — docs | /api/[vN/]webhook[s]/<token>/{event,events,callback,delivery,deliveries} answers 200 {"ok":true,"deliveryId":"<uuid4>","receivedAt":…} instead of a 404, keeping the delivery body (webhookBodyPreview) and the sender's own token (webhookToken) — tooling that posts its results to a collector sometimes posts them here instead, and a 404 leaves only a body hash. The subscription itself (/api/v1/webhook/<token>) and the management collection (/api/v1/webhooks) are different surfaces and stay unclaimed. Matching is on path shape alone, so nothing about the token changes the response; when the token is credential-shaped the canary-echo observer classifies it on the same line. Acknowledgement carries no credential-shaped field, so it needs no upstream key |
no |
| Billing / payment-method API — docs | Answers an unauthenticated write to a stored-payment-method endpoint with 201 and a created payment-method object, instead of the 404 that tells the sender the host is not misconfigured. Claims billing leaf names (payment-methods, cards, update-card, change_payment, … under any [/api][/vN][/<owner>] prefix) on path alone; generic spellings like /api/v1/account only when the body actually carries a card, so an ordinary account endpoint is never shadowed. Logs the sender's schema (paymentBodyFields) and two discriminating booleans — paymentCardTestRange (a documented processor test number, i.e. a catalogue sweep) versus paymentCardLuhnValid without it (a card-shaped number nobody publishes). A submitted card number is never written down: only its last four digits reach the log or the response, and the body preview redacts card-shaped runs first. Identifiers are per-request random and no canary is issued, so it needs no upstream key |
no |
All traps log one JSON line per event to the configured log path. See
LOGS.md for the schema.
pip install .Or in place (needs aiohttp on the path):
pip install aiohttp
python -m fluxFlux listens on 127.0.0.1:18081 by default. The expected deployment puts
nginx in front and proxies a set of trap paths (e.g. /.env, /.git/*,
/shell.php, etc.) to it; nginx handles TLS, X-Forwarded-* headers, and
all non-trap routing.
export TRACEBIT_API_KEY=... # optional — enables canary-backed traps (/.env, /.git/*, etc)
python -m fluxDocs: CONFIG.md (env vars) ·
LOGS.md (JSONL schema + result tags) ·
BENCH.md (throughput + tarpit saturation numbers).
A trap renderer that ships a fixed literal credential (hardcoded DB password, hardcoded API key) provides no detection value — a replay triggers nothing — and ships the same string across every sensor in the fleet, which becomes a cross-sensor fingerprint. Every secret-shaped field in a rendered response must therefore be per-hit unique. We back it with Tracebit when that adds detection value (replay against AWS STS, the Tracebit-hosted gitlab URL, the Tracebit sshIp); when there's no matching canary type we fall back to a per-hit random synthetic. Concretely, every secret-shaped field is either:
- A per-request Tracebit canary —
_aws(r)or_gitlab_creds(r, ...). Fires when replayed against the matching target (AWS STS globally, the Tracebit-hosted gitlab URL for u/p and cookie, the Tracebit sshIp for ssh). - A per-hit random synthetic —
_fake_db_password()or similar. Does not fire (no Tracebit path exists for the cred type, e.g. MySQL/Postgres), but is unique per rendering so the rendered body can't be fingerprinted across the fleet.
Hardcoded literals in the "plausible filler" around the canary are
fine when the value isn't credential-shaped — usernames like
wp_prod, host names like db.internal, bucket names, comments.
What must never be fixed is anything that looks like a password,
token, or key.
When the canary type doesn't exist in Tracebit Community yet (LLM API keys, Google service accounts), the trap must either (a) dress an AWS canary in plausible shape so a field-name-keyed harvester still exfils a live canary value (see the AI-credential traps below) or (b) emit a per-hit random synthetic so the response isn't fingerprintable — never a fixed literal.
All gated on TRACEBIT_API_KEY, with per-IP TTL caching to protect quota.
Toggle the whole category with CANARY_TRAPS_ENABLED. Paths are
case-insensitive exact matches.
"home-dir spellings" below means the credential file is answered
under every account name in _CRED_HOME_DIRS — /root, a bare
/home, the distro cloud-image defaults, the CI/CD runner accounts
(runner, github-runner, gitlab-runner, circleci, jenkins),
the managed-service accounts (ssm-user, cloud-user, azureuser),
the data/ML accounts (jovyan, sagemaker-user, airflow, hadoop,
glue_user) and the generic app-service accounts. The set is driven
from one list rather than enumerated per trap, because a host that
answers the file under one account and 404s the byte-identical request
under another is separable from a real filesystem by the sweep that
walks both in a single pass. Every one of these is also readable
through the /@fs/ dev-server surface, which resolves against the
same table.
| Trap | Paths | Canary type | Log tag |
|---|---|---|---|
| Shell rc files — docs | /.bashrc, /.bash_profile, /.bash_login, /.profile, /.zshrc, /.zprofile, /.zshenv, /.kshrc, /.cshrc, plus /root/… and /home/ubuntu/… home-dir spellings and the app-layout prefixes of .bashrc / .profile |
aws |
shell-rc |
| AWS credentials file (INI) | /.aws/credentials plus .bak / .old editor-leftover variants, home-dir spellings, the bare webroot-dropped /credentials, and undotted / "internal-route" spellings (/aws/credentials, /aws/iam/temporary-credentials, /aws_credentials.txt, /{data,hidden,tmp,vendor,internal,internal-api,s3,api,admin}/…) |
aws |
aws-credentials-file |
| AWS STS session-token envelope (JSON) | /aws.json, /aws-credentials.json, /aws_credentials.json, /.aws/credentials.json, plus the non-dotfile and API-route spellings scanners walk alongside them (/aws/credentials.json, /aws/iam/credentials.json, /aws/iam/temp-creds.json, /aws/s3/credentials.json, /s3-credentials.{json,bak}, /api/aws/credentials, /api/v1/aws/credentials, /api/v1/credentials, /private/aws_credentials.json, /secrets/aws.json, /.well-known/credentials.json) |
aws |
aws-credentials-json |
| AWS SDK config (INI) | /.aws/config, plus home-dir spellings and the app-layout prefixes — the sweep asks for config and credentials under the same account in one pass |
aws |
aws-config-file |
AWS Console-downloaded credentials.csv (IAM-user-creation) — docs |
/credentials.csv, /aws-credentials.csv, /aws_credentials.csv, /new_user_credentials.csv, /iam-credentials.csv, /iam_credentials.csv, plus webroot-prefix /admin/, /users/, /iam/, /app/, /backend/, /api/, /private/, /backup/ variants |
aws |
aws-credentials-csv |
| AWS Console "Create access key" CSV (two-column) — docs | /accesskeys.csv, /access_keys.csv, /access-keys.csv, /accesskey.csv, /rootkey.csv, /root_key.csv, /root-key.csv, /aws-access-keys.csv, /aws_access_keys.csv |
aws |
aws-access-keys-csv |
AWS Python SDK / gsutil boto config |
/.boto, /.boto3, /root/.boto, /home/.boto |
aws |
boto-config |
| AWS Amplify CLI project config | /.amplifyrc |
aws |
amplifyrc |
| Terraform tfstate (JSON) — docs | /.terraform/terraform.tfstate, /terraform.tfstate, /terraform.tfstate.backup |
aws |
terraform-tfstate |
| Terraform tfvars (HCL + JSON) — docs | /terraform.tfvars, /.terraform/terraform.tfvars, /terraform.tfvars.json, /.terraform/terraform.tfvars.json |
aws |
terraform-tfvars, terraform-tfvars-json |
| GCP service-account JSON (generic webroot aliases) — docs | /gcp-credentials.json, /config/gcp-credentials.json, /private/gcp-credentials.json, /api/credentials.json, /private/credentials.json, /backend/credentials.json, /app/credentials.json, plus the gcloud-created directory spelling /.gcloud/credentials, the bare hand-download filenames /sa.json, /gcp-service.json, /gc-service.json, and /secrets/gcp.json |
aws |
gcp-credentials-json |
| Niche cloud CLI configs — docs | OCI (/.oci/config, /.oci/oci_api_key.pem), Hetzner (/.config/hcloud/cli.toml), Civo, Exoscale, Scaleway, Fly.io, OVH, OpenStack, Terraform Cloud (/.terraform.d/credentials.tfrc.json, /.terraformrc), Pulumi, DigitalOcean doctl, Linode, s3cmd, s3fs, Cargo, RubyGems, GitHub CLI, 1Password, Cloudflare Tunnel, WireGuard, Headscale |
aws |
oci-config, hcloud-cli, civo-cli, exoscale-cli, scaleway-cli, fly-cli, ovh-conf, openstack-clouds-yaml, terraform-credentials-tfrc, terraformrc, pulumi-credentials, doctl-config, linode-cli, s3cfg, passwd-s3fs, cargo-credentials, gem-credentials, gh-hosts-yml, 1password-config, cloudflared-config, wireguard-conf, headscale-config |
| Azure CLI credential / profile cache — docs | /.azure/azureProfile.json, /.azure/accessTokens.json, /.azure/msal_token_cache.json, /.azure/service_principal_entries.json, /.azure/config, /.azure/clouds.config; the two token caches and /.azure/credentials also under home-dir spellings, since multi-cloud sweeps walk the .azure/ sibling under the same accounts as .aws/ |
aws |
azure-cli-profile, azure-cli-access-tokens, azure-cli-msal-cache, azure-cli-service-principal, azure-cli-config, azure-cli-clouds-config |
| Postgres pgpass | /.pgpass |
gitlab-username-password |
pgpass |
Apache .htpasswd — docs |
/.htpasswd |
gitlab-username-password |
htpasswd |
| WordPress config | /wp-config.php plus editor-leftover suffix variants (.bak, .save, .swp, .swo, .old, .orig, .txt, ~, ::$DATA) and short/relocation forms (/wp-config.bak, /wp-config.old, /wp-config.txt, /wp-config-backup.php, /backup/wp-config.php), absolute-webroot path-traversal variants (/var/www/wp-config.php, /var/www/html/wp-config.php, /srv/www/wp-config.php, /srv/http/wp-config.php, /usr/share/nginx/html/wp-config.php, plus /wp/ and /wordpress/ subdirs under /var/www/html/); also matches the observed double-encoded .bak form |
aws |
wp-config |
| Drupal settings.php — docs | /sites/default/settings.php plus editor-leftover suffix variants (.bak, .save, .swp, .swo, .old, .orig, .txt, ~), short/null-byte/space-truncation forms (/sites/default/settings.bak/.old/.txt, /sites/default/settings.php%00, /sites/default/settings.php%20), the unconfigured-template name (/sites/default/default.settings.php), the multisite /sites/all/settings.php shape, webroot-prefix /drupal/, /cms/ variants, and absolute-webroot path-traversal variants (/var/www/sites/default/settings.php, /var/www/html/sites/default/settings.php, /srv/www/sites/default/settings.php) |
aws |
drupal-settings-php |
Magento 2 app/etc/env.php — docs |
/app/etc/env.php plus editor-leftover suffix variants (.bak, .old, .save, .swp, ~, .dist), the sample-template shape (/app/etc/env.sample.php), sub-path deploy prefixes (/magento/, /magento2/, /shop/, /store/), Magento 1.x sibling filenames (/app/etc/local.xml, /app/etc/config.php), absolute-webroot path-traversal variants (/var/www/{,html/}app/etc/env.php, /srv/www/, /usr/share/nginx/html/, /var/www/html/magento{,2}/), and a double-encoded webroot form; PHP-array env.php whose system.default.aws_s3 slot carries the AWS canary alongside per-hit random crypt.key, DB/session/AMQP passwords, admin_url_secret, and admin frontName |
aws |
magento-env |
| Rails secrets bundle — docs | /config/database.yml (plaintext DB YAML — AWS canary in the production.s3_bucket_* slot), /config/secrets.yml (pre-5.2 plaintext — AWS canary in the production.aws block, per-env secret_key_base), /config/master.key (per-hit random 32-hex), and /config/credentials.yml.enc (per-hit random GCM-shaped <b64>--<b64>--<b64> blob); each with editor/backup variants (.example, .sample, .dist, .default, .bak, .old, .save, .orig, .swp, ~), the database dialect siblings (.pgsql, .postgres, .postgresql, .mysql, .sqlite, .sqlite3), Rails 6+ multi-environment /config/credentials/{production,staging,development,test}.{key,yml.enc}, the webroot-dropped leaf sweeps ask for more often than the canonical path (/database.yml, /database.yaml, /db.yml) and the application-prefixed spellings where the prefix replaces config/ rather than preceding it (/{api,app,backend,server,services,src,db,conf}/database.yml), and the shared _app_layout_variants webroot-prefix matrix (/app/, /storage/, /backend/, /backup/, /public/, /public_html/, /www/, /htdocs/) |
aws |
rails-database-yml / rails-secrets-yml / rails-master-key / rails-credentials-enc |
| Application config (generic) — docs | The framework-agnostic config-file dictionary that sweeps walk alongside the framework-specific files above, grouped by serialization format rather than framework: PHP (/config.php, /configuration.php, /settings.php, /config/config.php, /includes/config.php), PHP-by-concern — the whole of a stock Laravel config/ directory rather than a subset of it, since a real install has every member and the misses are what give the hits away: /config/database.php, /config/mail.php + mailer/email/smtp, /config/services.php + api/keys/credentials/app, /config/filesystems.php (where the object-store key pair actually lands), /config/queue.php + horizon (SQS pair plus the queue Redis password), /config/broadcasting.php + websockets, and the structural members /config/{session,logging,view,hashing,auth,cors,sanctum}.php — served for coherence, holding no credentials and spending no canary, Python (/settings.py, /config.py, /instance/config.py, /local_settings.py, plus the core/, backend/, src/, app/, config/, project/ package layouts), YAML (/config.yaml, /config.yml, /secrets.yml, /bootstrap.yml, /config/storage.yml, the Helm chart-values family /values.yaml + /{helm,chart,charts,deploy,k8s}/values.yaml and the per-environment overlays /values-{prod,production,staging}.yaml where the real credentials sit, and /credentials.{yml,yaml} — the YAML sibling of the JSON credentials file), TOML (/config.toml, /settings.toml, /netlify.toml), JSON (/env.json, /local.settings.json, /app-config.json, /push_config.json) plus the unauthenticated runtime-introspection endpoints (/api/config, /api/settings, and the /api/v{1,2}/{config,settings,env} matrix), and Java properties (/bootstrap.properties, /config.properties, /config/application.properties, /gradle.properties, /.gradle/gradle.properties); the Spring application.<ext> / bootstrap.<ext> traps additionally answer every -<profile> spelling (prod, production, dev, development, staging, stage, test, local, qa, uat, default), which is where Spring puts the values that differ per environment and therefore the credentials; every path in the family carries the editor/backup sibling set (.bak, .old, .save, .orig, .swp, ~) — filled by a rule over the family rather than written per path, so a path added later gets its siblings automatically and a sibling another trap already owns keeps its owner — plus the shared _app_layout_variants webroot-prefix matrix. Every body is format-correct — the PHP parses as PHP, the TOML round-trips through a TOML parser, the Python compiles — so a harvester that loads the file rather than grepping it gets a working config |
aws |
app-config-php / app-config-php-database / app-config-php-mail / app-config-php-services / app-config-php-filesystems / app-config-php-queue / app-config-php-broadcasting / app-config-php-structural / app-config-python / app-config-yaml / app-config-toml / app-config-json / app-config-properties |
| SQL dump | /backup.sql, /db.sql, /dump.sql, /database.sql, /backup/db.sql, /sql/backup.sql |
aws |
sql-dump |
| Generic JSON config | /config.json, /settings.json, /credentials.json, /secrets.json |
aws |
config-json |
| SFTP deploy config | /.vscode/sftp.json, /sftp-config.json, /sftp.json, /.ftpconfig; the host field names the host the request arrived for rather than a placeholder, so the target is one the finder can actually reach |
gitlab-username-password |
sftp-config |
| Deploy-sync config — docs | /remote-sync.json, /ftp-sync.json, /deployment-config.json, /deploy-config.json, /deploy.json, /ftpsync.settings, /.ftpsync.settings, /.vscode/remote-sync.json, /.vscode/deployment-config.json — editor-plugin deploy config naming an SFTP target on port 22 at the requested host, with a per-hit deploy_<8 hex> username derived from requestId (so a later SSH authentication attempt carrying it is traceable to one trap request) and a per-hit random password. No Tracebit canary types, therefore no upstream call |
— | deploy-sync-config |
| Firebase / GCP SA | /firebase.json, /firebase-config.json, /google-services.json, /serviceaccount.json, /service-account.json, /firebase-adminsdk.json, /gcp-service-account.json, /.config/gcloud/application_default_credentials.json plus its home-dir spellings, and the undotted / flattened fallbacks a harvester emits when it cannot place the real ~/.config/gcloud/ (/gcloud/application_default_credentials.json, /gcloud/credentials.json, /gcloud.json) |
aws |
firebase-json |
| Docker client | /.docker/config.json, /docker/config.json, /root/.docker/config.json, /home/.docker/config.json |
aws |
docker-config |
| Docker Compose | /docker-compose.yml, /docker-compose.yaml, /compose.yml, /compose.yaml, plus .prod, .production, .dev, .staging, .override variants (both .yml and .yaml) |
aws |
docker-compose |
| GitHub Actions workflows | /.github/workflows/{deploy,main,ci,build,test,docker,release,cd,publish}.yml plus .yaml variants |
aws |
github-actions-workflow |
| GitLab CI config | /.gitlab-ci.yml, /.gitlab-ci.yaml, /.gitlab/.gitlab-ci.yml |
aws |
gitlab-ci |
| Jenkins Pipeline | /Jenkinsfile, /Jenkinsfile.bak |
aws |
jenkinsfile |
| Jenkins credential store — docs | /credentials.xml, /jenkins/credentials.xml, /.jenkins/credentials.xml, /jenkins_home/credentials.xml, /var/jenkins_home/credentials.xml, /var/lib/jenkins/credentials.xml — the global credential store, with {…} ciphertext on the entries a real instance encrypts and the canary in the clear on the AWS entry (whose accessKey is plaintext in a real file) |
aws |
jenkins-credentials-xml |
Jenkins master.key — docs |
/secrets/master.key, /jenkins/secrets/master.key, /.jenkins/secrets/master.key, /jenkins_home/secrets/master.key, /var/jenkins_home/secrets/master.key, /var/lib/jenkins/secrets/master.key — per-hit key material, no canary; coming back for it after taking the store is what separates a scraper from an operator |
— | jenkins-master-key |
| Jenkins root config — docs | /jenkins/config.xml, /jenkins/config.xml.bak, /.jenkins/config.xml, /config/jenkins.xml, /config/jenkins.conf, /jenkins_home/config.xml, /var/jenkins_home/config.xml, /var/lib/jenkins/config.xml — no credential, no upstream call; the file that makes the credential store the obvious next request |
— | jenkins-config-xml |
| Bitbucket Pipelines | /bitbucket-pipelines.yml, /bitbucket-pipelines.yaml |
aws |
bitbucket-pipelines |
| Generic CI deploy config | /appveyor.yml, /.circleci/config.yml, /azure-pipelines.yml, /deployment.yml, /deploy.yml, /drone.yml, /.drone.yml, /.travis.yml, /cloudbuild.yaml plus .yaml/.yml variants where applicable |
aws |
generic-ci-config |
| CI/CD deploy descriptors (Azure node config, SDK-auth SP dump, CodeBuild/CodeDeploy specs) — docs | /azure.json, /etc/kubernetes/azure.json, /azure-credentials.json, /azure_credentials.json, /buildspec.yml, /appspec.yml plus .yaml and app-layout prefix variants |
aws (none for appspec) |
azure-node-json, azure-credentials-json, codebuild-buildspec, codedeploy-appspec |
| PM2 process definition | /ecosystem.config.js, /ecosystem.config.cjs, /ecosystem.json, /pm2.config.js, /pm2.json plus app-layout prefixes |
aws |
pm2-ecosystem-config |
| SPA build manifest and the chunk it names — docs | /.vite/manifest.json plus dist/ build/ public/ static/ placements; /dist/manifest.json, /build/manifest.json, /static/manifest.json, /assets/manifest.json; /manifest.webmanifest, /site.webmanifest plus dist/ build/ placements; /assets/env-config-<hash>.js plus dist/ build/ public/ static/ placements |
aws (chunk only; manifests issue none) |
spa-build-manifest, spa-webmanifest, spa-config-chunk-referenced, spa-config-chunk-foreign |
| Spring properties | /application.properties |
aws |
application-properties |
| Spring YAML | /application.yml, /application.yaml |
aws |
application-yml |
Spring Boot Actuator /env |
/actuator/env, /actuator/env.json, /env, /manage/env, /management/env, /api/actuator/env, /app/actuator/env, /backend/actuator/env |
aws |
actuator-env |
| Spring Boot Actuator surface — docs | /actuator/{heapdump,dump,configprops,health,healthcheck,mappings,threaddump,logfile,trace,httptrace,httpexchanges,jolokia,flyway,scheduledtasks,refresh} (httpexchanges is Boot 3's third name for trace/httptrace; plus standalone /jolokia, /api/jolokia, /monitoring/jolokia and their /list — operations beneath a mount are served by the Jolokia protocol handler) and the /manage, /management, /api/actuator, /app/actuator, /backend/actuator reverse-proxy aliases |
aws |
actuator-heapdump / actuator-configprops / actuator-health / actuator-mappings / actuator-threaddump / actuator-logfile / actuator-trace / actuator-jolokia / actuator-flyway / actuator-scheduledtasks / actuator-refresh |
| Production .env and its sibling rotation/backup/per-env variants | /.env.{production,prod,live,local,dev,development[.local],test[.local],staging,stage,uat,preprod,qa,ci,save,private,docker,override,example[.local],sample,remote,dist,bak,backup[1|2],old,orig,swp,~,json,yaml,yml,txt}, /.env{1,2,_bak,_old,_orig,_copy,_priv,_example}, /.environ, /env.{bak,txt,old,save,backup}, plus the cross-product against ~140 webroot prefixes (/{wp,wordpress,laravel,symfony,magento,drupal,api,backend,frontend,public,www,html,admin,dashboard,dev,prod,staging,docker,k8s,terraform,jenkins,mysql,postgres,redis,vendor,vite,nuxt,next,…}/.env*) so off-the-shelf env-harvester dictionary walks land on a canary on first match |
aws |
env-production |
Mail-service .env — docs |
/{sendgrid,postmark,mailjet,brevo,mailgun,mailing,mail,mailserver}/.env — service-specific API key formats (SendGrid SG.xxx, Postmark token, Mailjet key pair, Brevo xkeysib-, Mailgun key-) alongside AWS canary |
aws |
mail-service-env |
| dotenv-vault file — docs | /.env.vault, /.env.vault.bak, /.env.vault.example |
aws |
env-vault |
Process / system environment (/proc/<pid>/environ, /etc/environment) |
/proc/self/environ, /proc/1/environ, /proc/curproc/environ, /etc/environment, bare /environ, /environment — NUL-separated env block with AWS canary in AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY slots (matches the on-disk /proc/<pid>/environ shape; harvester loops grep raw bytes for AKIA…) |
aws |
proc-environ |
| Laravel debug log — docs | /storage/logs/laravel.log plus .bak / .old log-rotate siblings and absolute-webroot variants (/var/www/html/, /var/www/, /srv/www/html/, /app/, /home/laravel/); Monolog-shaped INFO + NOTICE + ERROR sequence whose QueryException ERROR line carries the AWS canary inside the JSON context.$_ENV block — the slot APP_DEBUG=true Laravel apps surface via HandleExceptions when an uncaught exception fires |
aws |
laravel-log |
WordPress wp-content/debug.log |
/wp-content/debug.log plus .bak / .old log-rotate siblings, the /wp-content/uploads/debug.log rotation-plugin sibling, WP-in-subdirectory prefix variants (/blog/, /wordpress/, /wp/, /news/, /site/, /cms/, /shop/), and absolute-webroot path-traversal variants (/var/www/, /var/www/html/, /srv/www/, /usr/share/nginx/html/); a PHP fatal-error trace whose tail dumps a wp-config context block carrying the AWS canary alongside per-hit synthetic DB_PASSWORD / AUTH_KEY / SECURE_AUTH_KEY / LOGGED_IN_KEY |
aws |
wp-debug-log |
| Kubernetes kubeconfig | /.kube/config plus .bak / .old siblings, /.kube/kubeconfig, bare /kubeconfig{,.yaml,.yml}, /config.kubeconfig, the /root/.kube/config / /home/{ubuntu,ec2-user,admin,app,node,deploy,www-data}/.kube/config home-dir prefix matrix, leading-dot /.kubeconfig, dash-separated /kube-config, kubectl-file /kubectl.{yaml,yml}, subdir aliases (/config/kubeconfig, /admin/kubeconfig, /kubernetes/config), and the Rancher /api/v1/clusters/kubeconfig/k8s download endpoint; an EKS-shaped kubeconfig YAML whose user exec: block sets the AWS canary as AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY in the aws-iam-authenticator env, with per-hit random bearer token, CA/client cert/key base64 blobs, account-id and cluster-id |
aws |
kubeconfig |
| Kubernetes Secret / Deployment / ConfigMap manifest — docs | /kubernetes.{yaml,yml}, /kubernetes/{secrets,secret,deployment,configmap}.{yaml,yml}, /k8s/{secrets,secret,deployment}.{yaml,yml}; multi-document YAML with the AWS canary base64-encoded in the Secret data: block and plaintext in the Deployment env: block so both base64-decoding and raw-AKIA* harvesters pick it up |
aws |
k8s-secret-manifest |
| AWS Amplify build output | /aws-exports.js + /src/, /static/js/, /assets/, /build/, /dist/ prefixes; /amplifyconfiguration.json, /awsconfiguration.json (+ /src/, /assets/) |
aws |
aws-amplify-exports-js / aws-amplify-config-json |
| Serverless Framework service config | /serverless.yml, /serverless.yaml, /api/serverless.yml, /backend/serverless.yml, /.serverless/serverless-state.json, /.serverless/serverless.yml — canary in the provider.environment block |
aws |
serverless-config |
| Vite dev-server env-leak — docs | /@vite/env — ES-module body with context.define flat-keys exposing VITE_* env vars; AWS canary lives in VITE_AWS_* and VITE_API_KEY slots |
aws |
vite-env |
Vite /@fs/ arbitrary file read — docs |
/@fs/<absolute path> — resolved against the whole trap table by suffix walk, not by an enumerated path list, plus a fixed system-file list matched exactly (/etc/{passwd,shadow}, /etc/nginx/nginx.conf, php.ini at every packaged location, and the projected Kubernetes service-account volume) |
inherited from the resolved trap; none for a system file | vite-fs-<trap>, vite-fs-etc-passwd, vite-fs-etc-shadow, vite-fs-etc-nginx-conf, vite-fs-etc-php-ini, vite-fs-k8s-serviceaccount-{token,namespace,ca-cert}, vite-fs-miss |
php://filter local file read — docs |
any query string containing php://filter, on any path — the resource is resolved by suffix walk against the whole trap table, plus the exact-matched system-file list (/etc/{passwd,shadow}, /etc/nginx/nginx.conf, php.ini at every packaged location, the projected Kubernetes service-account volume) |
inherited from the resolved trap; none for a system file | php-filter-<trap>, php-filter-issued, php-filter-etc-passwd, php-filter-etc-php-ini, php-filter-miss |
| Vercel project config — docs | /vercel.json plus runtime working-directory variants (/app/, /var/task/, /usr/src/app/, /srv/app/, /home/node/app/, /opt/app/, /workspace/) — Vercel vercel.json JSON with AWS canary in env, build.env, and per-route headers[].headers[] (x-aws-access-key-id slot) |
aws |
vercel-json |
| Go pprof debug endpoint — docs | /debug/pprof[/heap|/cmdline|/goroutine|/profile|/symbol|/trace|/threadcreate|/block|/mutex|/allocs] plus /api-prefixed variants |
aws |
pprof-dump |
| phpinfo() | /phpinfo.php, /info.php, /php.php, /test.php, /php_info.php, /phpinfo, /pinfo.php, /i.php, /pi.php, plus the long-tail aliases secrets sweeps walk (/php-info.php, /phpinfo2.php, /infophp.php, /infos.php, /iinfo.php, /phpversion.php, /php_version.php, /old_phpinfo.php, /linusadmin-phpinfo.php, /temp.php, /time.php, /asdf.php, /x.php, /1.php) and the .cgi spellings (/phpinfo.cgi, /info.cgi). The unambiguous phpinfo leaf names also answer under any parent directory up to four segments deep (/wp-admin/phpinfo.php, /cgi-bin/info.cgi, /crm/backend/phpinfo.php) — gated on the leaf rather than a parent vocabulary, because a phpinfo page belongs to no framework layout; the generic stems above stay root-only — docs |
aws |
phpinfo |
| Diagnostic-script source (editor-backup spellings) — docs | phpinfo.php / info.php / php.php / test.php carrying a backup suffix (.bak, .old, .save, ~, .orig, .swp, .backup, .txt, .bak~, .old~, .save~, .tmp). Returns the source of a deployment-diagnostics script — a backup suffix takes the name outside the PHP handler, so a real host serves the bytes instead of executing them, and the credentials an operator typed inline are the disclosure. Cannot be reached by the shell-jacking sweep gate, whose matcher requires a .php ending |
aws |
phpinfo-source |
_environment route — docs |
/_environment plus the front-controller spellings /webroot/index.php/_environment, /index.php/_environment, /app.php/_environment, /public/index.php/_environment. Returns a KEY=value dump of the process environment. The framework is deliberately not claimed — the bare and prefixed spellings are one client testing two deployment layouts, not a product tell — so the body is the lowest common denominator every such route shares |
aws |
environment-dump |
| SSH private key | /id_rsa, /.ssh/id_rsa, /ssh/id_rsa, /ssh/id_rsa.key, /keys/id_rsa, /private.key, /deploy_key, /deploy.key, /.ssh/id_ed25519, /.ssh/id_dsa, /.ssh/id_ecdsa, /id_ed25519, /id_dsa, /id_ecdsa, /root/.ssh/id_rsa, /home/.ssh/id_rsa, plus user-named home-dir variants /home/{ubuntu,ec2-user,admin,app,node,deploy,www-data}/.ssh/id_rsa, plus broad-dict variants /.ssh/identity{,_key,.key}, /.ssh/{id_rsa,id_dsa,id_ed25519,id_ecdsa}_key, /.ssh/{id_rsa,id_dsa}.priv, /.ssh/id_dsa.key~, plus FIDO2 security-key variants /.ssh/{id_ed25519,id_ecdsa}_sk, plus the web-server TLS key filenames /server.key, /server.pem, /privatekey.key, /private-key, /host.key, /localhost.key, /key.pem, /privkey.pem and their ssl/, certs/, private/, keys/, nginx/ssl/, conf/ssl/ directory variants |
ssh |
ssh-private-key |
| SSH public key | /id_rsa.pub, /.ssh/id_rsa.pub |
ssh |
ssh-public-key |
| SSH client config | /.ssh/config |
ssh |
ssh-config |
| known_hosts | /.ssh/known_hosts, /known_hosts, /.ssh/know_hosts (typo variant) |
ssh |
known-hosts |
| authorized_keys | /authorized_keys, /.ssh/authorized_keys, /.ssh/authorized_keys2, /static/.ssh/authorized_keys, /downloads/.ssh/authorized_keys, /blog/.ssh/authorized_keys |
ssh |
authorized-keys |
| .netrc | /.netrc, /_netrc |
gitlab-username-password |
netrc |
| git credential store | /.git-credentials, /root/.git-credentials, /home/.git-credentials; fake-git also serves /.git/credentials |
gitlab-username-password |
git-credentials / fake-git |
| .npmrc | /.npmrc, /root/.npmrc, /home/.npmrc |
gitlab-username-password |
npmrc |
| Node.js dependency manifests — docs | /yarn.lock(.bak|.old), /package-lock.json(.bak|.old), /var/backups/npm/package-lock.json.old, /package.json, /.yarnrc, /.yarnrc.yml |
gitlab-username-password |
yarn-lock / package-lock-json / package-json / yarnrc / yarnrc-yml |
| Non-Node dependency manifests — docs | `/composer.json(.bak | .old | .save), /composer.lock(.bak |
| .pypirc | /.pypirc |
gitlab-username-password |
pypirc |
| GitLab API user | /api/v4/user |
gitlab-username-password |
gitlab-api-user |
| GitLab sign-in | /users/sign_in |
gitlab-cookie |
gitlab-sign-in |
| Heroku Procfile — docs | /Procfile |
aws |
procfile |
| Heroku container manifest — docs | /heroku.yml, /heroku.yaml |
aws |
heroku-yml |
| Heroku app metadata — docs | /app.json |
aws |
heroku-app-json |
| .NET Core appsettings — docs | /appsettings.json plus .production.json, .development.json, .staging.json, .local.json variants |
aws |
appsettings-json |
| IIS web.config — docs | /web.config plus editor-leftover suffix variants (.bak, .old, .orig, .save) |
aws |
iis-web-config |
| PHP Composer auth.json — docs | /auth.json, /.hermes/auth.json |
gitlab-username-password |
composer-auth-json |
| VS Code debug-launch config | /.vscode/launch.json, /.vscode/tasks.json, plus app-layout prefixed variants. The env block of a launch configuration is where a developer parks the credentials a debug run needs, and .vscode/ is not in most default ignore files |
aws |
vscode-launch-json |
| Firebase Hosting SDK config | /__/firebase/init.json, /__/firebase/init.js — the client config a hosted app fetches from Firebase Hosting's reserved namespace. Distinct from the service-account family above: this one confirms the site is a live Firebase project and carries the API key |
aws |
firebase-init-json, firebase-init-js |
| Elixir / Phoenix runtime config | /config/runtime.exs, /config/prod.exs, /config/releases.exs, /config/dev.exs, /config/config.exs, plus app-layout prefixed variants. runtime.exs is evaluated on boot, which is why it is the sibling that holds secret_key_base, the database URL and cloud credentials |
aws |
elixir-config-exs |
| Dockerfile source — docs | /Dockerfile plus environment-suffixed variants (.prod, .production, .dev, .development, .local, .staging, .worker, .build) and /Containerfile |
aws |
dockerfile |
| OpenAI config file | /.openai/config.json |
aws (†) |
openai-config |
| Anthropic config file | /.anthropic/config.json |
aws (†) |
anthropic-config |
| Cursor MCP config | /.cursor/mcp.json |
aws (†) |
cursor-mcp |
| Claude Code credentials | /.claude/.credentials.json, /.config/claude/.credentials.json, /.credentials.json, /.config/anthropic/credentials/default.json |
aws (†) |
claude-credentials |
| Claude Desktop settings | /.claude/settings.json |
aws (†) |
claude-settings |
| Cline settings | /.cline/settings.json |
aws (†) |
cline-settings |
| Generic MCP server configs — docs | /.cline/mcp_settings.json, /mcp_settings.json, /mcp.json, /.mcp/mcp.json, /.mcp/config.json, /.mcp/settings.json, /.cursor/mcp_config.json, /.mcp.json, /claude_desktop_config.json. Every rendered config advertises an HTTP-transport server pointing at this host's own /mcp endpoint, bearer'd with the per-hit canary, so a reader that follows the config lands on the JSON-RPC trap and the two hits join on the token |
aws (†) |
mcp-config |
| Anthropic dotenv fragment — docs | /.env.anthropic |
aws (†) |
anthropic-dotenv |
| OpenAI dotenv fragment — docs | /.env.openai |
aws (†) |
openai-dotenv |
| Continue.dev config | /.continue/config.json |
aws (†) |
continue-config |
| Sourcegraph Cody config | /.sourcegraph/cody.json |
aws (†) |
cody-config |
| Aider config | /.aider.conf.yml |
aws (†) |
aider-conf |
| Open-Interpreter config | /.config/open-interpreter/config.yaml |
aws (†) |
open-interpreter-config |
| LiteLLM proxy config | /litellm_config.yaml, /litellm/config.yaml, /proxy_config.yaml |
aws (†) |
litellm-config |
| LangSmith env | /langsmith.env |
aws (†) |
langsmith-env |
| HuggingFace token | /.huggingface/token, /.cache/huggingface/token |
aws (†) |
huggingface-token |
| Streamlit secrets | /.streamlit/secrets.toml |
aws (†) |
streamlit-secrets |
| OpenAI flat config | /openai.json |
aws (†) |
openai-config-flat |
| Anthropic flat config | /anthropic.json |
aws (†) |
anthropic-config-flat |
| Generic AI provider config | /cohere_config.json, /tabnine_config.json, /.bito/config.json, /.codeium/config.json, /.roost/config.json, /pinecone_config.json, /.lobechat/config.json, /chatgpt-next-web.json |
aws (†) |
ai-provider-config |
| Baseten model deploy config | /baseten.yaml |
aws (†) |
baseten-config |
| OpenAI Codex CLI auth | /.codex/auth.json, /root/.codex/auth.json |
aws (†) |
codex-auth |
| OpenAI Codex CLI persistent config — docs | /.codex/config.toml, /root/.codex/config.toml |
aws (†) |
codex-config-toml |
| rclone remote credential store — docs | /rclone.conf, /.rclone.conf, /.config/rclone/rclone.conf, /root/.config/rclone/rclone.conf |
aws |
rclone-conf |
| Gemini CLI OAuth creds | /.gemini/oauth_creds.json, /root/.gemini/oauth_creds.json |
aws (†) |
gemini-oauth-creds |
| Gemini CLI settings | /.gemini/settings.json, /root/.gemini/settings.json |
aws (†) |
gemini-settings |
| AI-IDE workspace rules | /.cursorrules, /.clinerules, /.windsurfrules |
aws (†) |
ai-ide-rules |
| Cursor IDE state DB | /.cursor/User/globalStorage/state.vscdb |
aws (†) |
cursor-state-vscdb |
| DashScope plain api_key | /.dashscope/api_key |
aws (†) |
dashscope-api-key |
| Anthropic plain api_key | /.anthropic/api_key |
aws (†) |
anthropic-api-key |
| DeepSeek config | /.deepseek/config.json |
aws (†) |
deepseek-config |
| Kimi / Moonshot credentials | /.kimi/credentials/kimi-code.json, /.kimi/kimi-code.json, /.moonshot/settings.json |
aws (†) |
kimi-credentials |
| OpenClaw config | /.openclaw/openclaw.json, /root/.openclaw/openclaw.json |
aws (†) |
openclaw-config |
| OpenCode config | /root/.config/opencode/config.json |
aws (†) |
opencode-config |
| vast.ai credentials | /root/.config/vastai/credentials.json |
aws (†) |
vastai-credentials |
| Nerve agent config | /root/.nerve/config.yaml |
aws (†) |
nerve-config |
| Spawn CLI rc | /root/.spawnrc |
aws (†) |
spawnrc |
| MoltBook credentials | /root/.config/moltbook/credentials.json |
aws (†) |
moltbook-credentials |
| Claude Code top-level config | /.claude.json, /root/.claude.json, /.claude/config.json, /.claude/settings.local.json |
aws (†) |
claude-config |
| Claude Code history | /.claude/history.jsonl |
aws (†) |
claude-history |
| Root-home Claude credentials | /root/.claude/.credentials.json |
aws (†) |
claude-credentials-root |
| Agent instruction files | /AGENTS.md, /.claude/CLAUDE.md, /root/.claude/CLAUDE.md |
aws (†) |
agents-md |
/users/sign_in returns the cookie canary as Set-Cookie: _gitlab_session=<value>. /api/v4/user embeds the username/password
canary as a plausible GitLab API user response.
The ssh canary fires only when the stolen key is replayed against
Tracebit's sshIp (returned alongside the keypair). That's why
ssh-config and known-hosts exist — without a target-host hint,
a harvested /id_rsa points at nothing, so an attacker runs
ssh -i id_rsa <arbitrary-host> and the canary never fires. The
three traps together (id_rsa → config → known_hosts) give
a scanner walking an exposed .ssh/ the full key + Host bastion HostName <sshIp> mapping, which resolves to an ssh bastion replay
the canary can catch.
† The AI-credential-file traps probably don't make sense yet.
Tracebit Community doesn't expose an OpenAI / Anthropic / LLM canary
type, so these traps dress an aws canary in OpenAI / Anthropic /
Cursor / Claude-shaped JSON. A scanner that filters by key-format prefix
(sk-..., sk-ant-...) will correctly decide the key is fake and
drop it; a scanner that harvests by field name (api_key, auth_token,
accessToken, GITHUB_PERSONAL_ACCESS_TOKEN) will still serialize the
value and ship it, and that side-channel trips the AWS canary if it's
ever used as AWS credentials. Shipped anyway because the probe itself
is what we want to log. Swap the renderers to real LLM canaries when
Tracebit ships them.
The four canary types (aws, ssh, gitlab-username-password,
gitlab-cookie) are everything Tracebit Community currently exposes via
/openapi.json. Email
and LLM canaries are hinted at in Tracebit marketing but not yet in the
API; new trap surfaces for those will land when the API does.
pip install -e '.[dev]'
python -m pytestTwo test files:
tests/test_server.py— pure-function tests (renderers, path matchers, parsers) + dispatch tests via aiohttp's in-processTestClient.tests/test_integration.py— binds flux to a random port on 127.0.0.1 and hits it with a real HTTP client over the kernel loopback. Catches anything that only breaks on a real socket.
Response shape, parsed fields, and rationale for the novel traps live
under docs/:
- Fake LLM-API endpoint
- Fake AI-gateway proxy admin API
- Fake GraphQL endpoint
- Fake SonicWall SSL VPN endpoint
- Fake Cisco WebVPN endpoint
- Fake Ivanti Connect Secure / Pulse Secure VPN endpoint
- Fake FortiGate SSL VPN endpoint
- Fake Palo Alto GlobalProtect gateway
- Fake Sophos XG SSL VPN
- Fake Barracuda SSL VPN
- Fake Check Point Mobile Access / Gaia
- Jenkins build-server state files
- Fake F5 BIG-IP APM / TMUI
- Fake Citrix NetScaler / Gateway portal
- Fake Microsoft RDWeb (RD Web Access) trap
- Fake ASP.NET
Trace.axddisclosure trap - Fake Microsoft Exchange (OWA / ECP / autodiscover / PSRemoting) trap
- Fake IBM Aspera Faspex trap
- Fake Hikvision IP camera trap
- Fake ONVIF device_service trap
- Fake D-Link / Linksys HNAP1 router trap
- Fake SOHO router / GPON-ONT web admin trap
- Fake GeoServer admin / OGC
- Fake Liferay Portal JSON Web Services
- Fake Gravity SMTP plugin (WordPress REST)
- Fake WordPress REST API index
- Fake WordPress REST Batch API
- WordPress REST route alias (
?rest_route=) - Fake Laravel Telescope debug panel
- Fake Laravel Debugbar stored-request browser
- Laravel debug log (
storage/logs/laravel.log) - Fake ColdFusion admin / component browser
- Fake Atlassian Confluence + Apache Struts OGNL
- Fake OIDC / OAuth discovery endpoint
- OIDC endpoints — the addresses the discovery document advertises
- Fake phpMyAdmin login page
.DS_Storefilesystem-metadata leak trap- Fake Oracle WebLogic Admin Console
- Fake Drupal
/user/register+ settings.php - Fake Joomla 4 public-config disclosure
- Tomcat
/..;/env.*path-normalization bypass - Fake Spring Cloud Gateway Actuator extension
- Jolokia JMX-over-HTTP operations
- Cmd-injection / printenv responder
- CI/CD config canaries
- Node.js dependency-manifest canary set
- Fake webshell
- Shell-jacking sweep gate
- WordPress plugin upload-vector matrix
- Code-execution and file-read API surface
- Self-referenced assets and form actions
The other traps (.env, /.git/, canary file traps, tarpit +
fingerprinting) are documented in CONFIG.md and the
canary table above.
MIT. See LICENSE.