{"id":25948788,"url":"https://github.com/snailsploit/snail-url","last_synced_at":"2026-05-12T19:11:07.833Z","repository":{"id":280304621,"uuid":"941558648","full_name":"SnailSploit/Snail-Url","owner":"SnailSploit","description":"Snail-Url is Yet another Wrapper for Open Redirect finding,","archived":false,"fork":false,"pushed_at":"2025-03-02T15:54:31.000Z","size":12,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-02T16:33:44.660Z","etag":null,"topics":["docker","openredirect-scanner","penetration-testing","ptsnails"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SnailSploit.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-03-02T15:25:35.000Z","updated_at":"2025-03-02T15:56:27.000Z","dependencies_parsed_at":"2025-03-02T16:33:46.523Z","dependency_job_id":"09afe6f7-5087-4399-be4c-36097c300170","html_url":"https://github.com/SnailSploit/Snail-Url","commit_stats":null,"previous_names":["snailsploit/snail-url"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SnailSploit%2FSnail-Url","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SnailSploit%2FSnail-Url/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SnailSploit%2FSnail-Url/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SnailSploit%2FSnail-Url/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SnailSploit","download_url":"https://codeload.github.com/SnailSploit/Snail-Url/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241837066,"owners_count":20028304,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","openredirect-scanner","penetration-testing","ptsnails"],"created_at":"2025-03-04T11:22:33.194Z","updated_at":"2026-05-12T19:11:07.825Z","avatar_url":"https://github.com/SnailSploit.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n\n# Snail-url\n\n**Async open-redirect hunter** for red teams and appsec engineers.\nCrawls, harvests redirect-prone URLs, injects payloads, and confirms real open redirects via HTTP 30x, HTML meta refresh, and common JS redirects.\n\n\u003e ⚠️ **Legal/Ethical**: Use only on assets you own or are authorized to test. You are responsible for complying with all laws and terms of service.\n\n---\n\n## Highlights\n\n* ⚡ **Fast \u0026 async** (aiohttp) with **per-host rate limiting**\n* 🧭 **Crawler fallback** (scope-aware) to discover targets when you don’t have a URL list\n* 🧪 **Payload strategy sets**: `basic`, `aggressive`, `stealth` (and custom test domain)\n* 🔐 **SAML / OIDC** focus: `RelayState`, `redirect_uri`, `returnTo`, `continue`, etc.\n* 📤 **Outputs**: JSON, NDJSON, CSV + optional dump of unverified candidates\n* 🖥️ **Pretty CLI**: compact, padded tables that don’t overflow your terminal\n\n---\n\n## Install\n\n### Local\n\n```bash\ngit clone https://github.com/\u003cyou\u003e/snail-url.git\ncd snail-url\npython3 -m venv .venv \u0026\u0026 source .venv/bin/activate\npip install --upgrade pip\npip install -e .\n```\n\n### Docker\n\n```bash\ndocker build -t snail-url:latest .\n# mount current folder for saving results\ndocker run --rm -v \"$(pwd)\":/scan snail-url:latest \\\n  snail-url -d example.com --crawl --max-pages 2000 --json-out /scan/results.json\n```\n\n---\n\n## Quick start\n\nBasic run (no crawl):\n\n```bash\nsnail-url -d example.com --json-out results.json\n```\n\nCrawler fallback + aggressive payloads:\n\n```bash\nsnail-url -d example.com \\\n  --crawl --max-pages 2000 \\\n  --payload-set aggressive \\\n  --include-subdomains \\\n  --samloidc \\\n  --pretty --max-col 100 \\\n  --json-out results.json --csv-out results.csv\n```\n\nUse a custom callback/test domain:\n\n```bash\nsnail-url -d example.com --test-domain https://red.example/cb --json-out results.json\n```\n\nScan a pre-collected list instead of crawling:\n\n```bash\nsnail-url --in urls.txt --json-out results.json --pretty\n```\n\nDump the **unverified** candidate list for manual review:\n\n```bash\nsnail-url -d example.com --crawl --candidates-out candidates.txt\n```\n\n---\n\n## CLI\n\n```\nsnail-url [-d DOMAIN] [--seed URL ...] [--in FILE] [--crawl]\n          [--include-subdomains] [--respect-robots]\n          [--max-pages N] [-c N] [--per-host N]\n          [--payload-set {basic,aggressive,stealth}]\n          [--test-domain URL]\n          [--json-out FILE] [--ndjson-out FILE] [--csv-out FILE]\n          [--candidates-out FILE] [--user-agent UA] [--samloidc]\n          [--pretty] [--no-banner] [--max-col N]\n```\n\n**Most useful flags**\n\n* `-d, --domain` — target registrable domain (sets default scope/seed)\n* `--seed` — one or more starting URLs (repeatable)\n* `--in` — file with URLs to check (one per line)\n* `--crawl` — enable crawler fallback (async, scope-aware)\n* `--include-subdomains` — widen scope to subdomains\n* `--max-pages` — crawl budget (default: 2000)\n* `-c, --concurrency` — global concurrency (default: 40)\n* `--per-host` — per-host concurrency limit (default: 10)\n* `--payload-set` — `basic` | `aggressive` | `stealth`\n* `--test-domain` — destination injected into redirect params\n* `--samloidc` — prioritize SAML/OIDC keys (`RelayState`, `redirect_uri`, etc.)\n* `--pretty`, `--max-col` — tidy, padded table output\n* `--json-out`, `--ndjson-out`, `--csv-out` — choose one or many\n\n---\n\n## What gets detected\n\n* **HTTP redirects**: 30x with `Location` pointing to your **test domain**\n* **HTML Meta Refresh**: e.g., `\u003cmeta http-equiv=\"refresh\" content=\"0;url=...\"\u003e`\n* **JS redirects**: `location =`, `location.replace()`, `location.assign()`\n\n**Redirect-ish parameters** (partial list):\n`redirect`, `redirect_uri`, `url`, `next`, `dest`, `destination`, `return`, `returnTo`, `continue`, `cb`, `RelayState`, etc.\n\n**SAML/OIDC** heuristics (when `--samloidc`):\n\n* Detects SAML-ish or OIDC-ish endpoints (`/saml/...`, `/oauth*/authorize`, `/callback`).\n* Elevates `RelayState` and `redirect_uri` for injection.\n\n---\n\n## Output examples\n\n### JSON\n\n```json\n[\n  {\n    \"original\": \"https://app.example.com/redirect?redirect_uri=/dashboard\",\n    \"confirmed\": true,\n    \"param\": \"redirect_uri\",\n    \"payload\": \"https://red.example/cb\",\n    \"mutated\": \"https://app.example.com/redirect?redirect_uri=https%3A%2F%2Fred.example%2Fcb\",\n    \"via\": \"http_redirect\",\n    \"status\": 302,\n    \"location\": \"https://red.example/cb\"\n  }\n]\n```\n\n### NDJSON\n\n```\n{\"original\":\"https://...\",\"confirmed\":true,\"param\":\"redirect\",\"via\":\"html/js_redirect\",\"status\":200,...}\n```\n\n### CSV (columns vary with data)\n\n```\nconfirmed,param,original,mutated,via,status,location,payload\ntrue,redirect_uri,https://...,https://...,http_redirect,302,https://red.example/cb,https://red.example/cb\n```\n\n---\n\n## How it works (high-level)\n\n```\nSeeds/URLs\n   └─► Crawler (optional) ─► Harvester (redirect-ish params)\n                              └─► Verifier (payload sets, no-follow)\n                                   └─► Findings (JSON/NDJSON/CSV)\n```\n\n* **Crawler**: async fetch, extracts links, respects scope, per-host RL.\n* **Harvester**: filters URLs with redirect-ish parameters.\n* **Verifier**: injects payloads → checks 30x `Location`, meta refresh, JS redirect.\n\n---\n\n## Tuning \u0026 tips\n\n* **Throughput**: Bump `-c` but keep `--per-host` reasonable (8–12) to reduce server strain.\n* **Noise**: Use `--payload-set stealth` for low-touch passes; `aggressive` when you need depth.\n* **Scope**: Start narrow (`-d`) and add `--include-subdomains` if needed.\n* **Crawler budget**: Increase `--max-pages` for large estates; capture candidates via `--candidates-out`.\n* **User-Agent**: Customize with `--user-agent` for logs/attribution.\n\n---\n\n## Troubleshooting\n\n* **No findings but many candidates**\n  Targets might block external destinations; try different `--payload-set` or `--test-domain`.\n* **Timeouts**\n  Reduce `-c` and `--per-host`; some WAFs throttle concurrency.\n* **Messy terminal output**\n  Use `--pretty --max-col 100` (table is padded \u0026 ellipsized). Use `--no-banner` to minimize whitespace.\n\n---\n\n## Development\n\n```bash\n# lint/format as you like; basic tests included\npytest -q\n```\n\nProject layout:\n\n```\nsnail_url/\n  cli.py          # CLI entry\n  crawler.py      # async crawler (scope-aware)\n  harvester.py    # picks redirect-like URLs\n  verifier.py     # injects payloads, confirms redirects\n  payloads.py     # basic/aggressive/stealth generators\n  saml_oidc.py    # SAML/OIDC heuristics\n  ui.py           # pretty table output\n  utils.py, constants.py, output.py\n```\n\n---\n\n## Roadmap\n\n* Optional archive sources (Wayback, GAU) as inputs\n* SARIF exporter for CI\n* Parameter mutation strategies by framework (Next.js, Spring, ASP.NET)\n* Allow/deny-lists per path/param\n\n---\n\n## Contributing\n\nPRs and issues welcome. Please include:\n\n* Clear repro steps (target pattern anonymized)\n* Before/after output snippets\n* Any performance/safety considerations\n\n---\n\n## License\n\nMIT © SnailSploit\n\n---\n\nWant me to drop this straight into your repo as `README.md` (and add status badges)? Say the word and I’ll prep a PR description and push-ready commit message.\n\n\u003c!-- snailsploit-backlink:start --\u003e\n\n---\n\n## 📚 Documentation \u0026 Author\n\nThis project's full writeup, methodology, and related research lives at:\n\n**[https://snailsploit.com/tools](https://snailsploit.com/tools)**\n\nCreated by **Kai Aizen** — independent offensive security researcher.\n\n[snailsploit.com](https://snailsploit.com) · [Research](https://snailsploit.com/research) · [Frameworks](https://snailsploit.com/frameworks) · [GitHub](https://github.com/SnailSploit) · [LinkedIn](https://linkedin.com/in/kaiaizen) · [ResearchGate](https://www.researchgate.net/profile/Kai-Aizen-2) · [X/Twitter](https://x.com/SnailSploit)\n\n\u003e *Same attack. Different substrate.*\n\n\u003c!-- snailsploit-backlink:end --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsnailsploit%2Fsnail-url","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsnailsploit%2Fsnail-url","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsnailsploit%2Fsnail-url/lists"}