Web cache poisoning - Lỗ hổng đầu độc bộ nhớ cache (phần 2)
IV. Phân tích và khai thác các lỗ hổng Web cache poisoning
1. Ví dụ về tìm kiếm unkeyed values
Như ở bài viết trước đã đề cập, việc tìm kiếm các giá trị unkeyed đóng vai trò quan trọng trong tấn công web cache poisoning. Trong bài lab Web cache poisoning with an unkeyed header, chúng ta sẽ sử dụng một extension "đắc lực" trong công cụ Burp Suite để tìm kiếm các giá trị unkeyed.
Quan sát response, chú ý rằng ứng dụng có thể tồn tại lỗ hổng web cache poisoning do tồn tại cơ chế caching:
Click chuột phải trong request, chọn đến extension Param Miner, tôi vẫn thường sử dụng lựa chọn Guess everything!
Chúng ta có thể xem kết quả tìm kiếm tại mục Target > Site map:
Trong trường hợp này, unkeyed value nghi ngờ là header X-Forwarded-Host
. Thử lại trong Repeater. Với header X-Cache
trong response nhận giá trị miss
, chuỗi viblo-security.com
hiển thị trong response.
Thay đổi một giá trị khác cho header X-Forwarded-Host
, response không đổi. Chứng tỏ header này không phải một cache key, hay nói cách khác nó là một giá trị unkeyed. Đồng thời được lưu trữ trong tài nguyên cache với thời gian giây.
Bạn đọc có thể thực hành tìm kiếm unkeyed value trong một trường hợp khác với bài lab Web cache poisoning with an unkeyed cookie.
2. Unkeyed query string
Xét ví dụ cấu hình cơ chế caching bằng Varnish Cache như sau:
sub vcl_recv {
# Allow caching of GET and HEAD requests
if (req.method != "GET" && req.method != "HEAD") {
return(pass);
}
# Cache only requests to the homepage
if (req.url !~ "^/$") {
return(pass);
}
# Enable caching
if (req.http.Cache-Control !~ "no-cache") {
unset req.http.Cookie;
set req.url = req.url + "?" + req.http.host;
return(hash);
}
# Don't cache requests with cookies
if (req.http.Cookie) {
return(pass);
}
}
sub vcl_backend_response {
# Enable caching for successful responses
if (beresp.status == 200) {
unset beresp.http.set-cookie;
set beresp.ttl = 10m;
set beresp.http.Cache-Control = "public, max-age=600";
}
}
Trong cấu hình trên, việc sử dụng unkeyed query string được thực hiện bằng cách thêm địa chỉ host vào cuối URL của request và sử dụng nó để lưu trữ các request trong bộ nhớ cache.
set req.url = req.url + "?" + req.http.host;
Đoạn mã này sẽ thêm địa chỉ host vào cuối URL của request và sử dụng nó làm key để lưu trữ trong bộ nhớ cache. Như vậy, các request có cùng địa chỉ host sẽ được lưu trữ trong cùng một bản sao cache. Do đó, nếu có nhiều request với các tham số truy vấn khác nhau, bởi có cùng địa chỉ host nên chúng sẽ luôn được lưu trữ trong cùng một bản sao cache.
Chẳng hạn, với cấu hình trên, có thể khai thác bằng cách sử dụng một trang web khác đưa vào URL của trang chủ, cùng với một query string chứa một payload tấn công XSS như sau:
https://example.com/?attacker.com?<script>alert('XSS')</script>
Khi request này được gửi đến Varnish Cache, nó sẽ được xử lý theo cấu hình của sub vcl_recv và được băm theo giá trị hash của URL là /attacker.com?<script>alert('XSS')</script>
. Sau đó, Varnish Cache sẽ kiểm tra cache để xem có trả về kết quả cho request này hay không. Nếu trong cache không có kết quả, Varnish Cache sẽ chuyển request này tới ứng dụng web backend để xử lý.
Ứng dụng backend sẽ nhận request này và thấy rằng URL được yêu cầu là /attacker.com?<script>alert('XSS')</script>
và xử lý như một request hợp lệ, gửi lại response chứa payload tấn công trả về cho Varnish Cache.
Cuối cùng, Varnish Cache sẽ lưu response này vào cache cùng với payload tấn công. Dẫn đến response chứa payload có thể được trả về cho tất cả các request tiếp theo đến từ người dùng khác.
Bạn đọc có thể luyện tập khai thác lỗ hổng web cache poisoning qua unkeyed query string trong bài lab Web cache poisoning via an unkeyed query string.
với cách cấu hình sai unkeyed query string trong Varnish Cache, chúng ta có thể dễ dàng ngăn chặn bằng cách loại bỏ các unkeyed query string. Ví dụ thay đổi cấu hình như sau:
sub vcl_recv {
if (req.url ~ "^\?.*") {
return(pass);
}
set req.http.X-Real-IP = client.ip;
set req.http.X-Forwarded-For = client.ip;
set req.http.host = regsub(req.http.host, ":[0-9]+", "");
set req.url = regsub(req.url, "\?.*", "");
return(hash);
}
sub vcl_hash {
hash_data(req.url);
if (req.http.host) {
hash_data(req.http.host);
}
if (req.http.Accept-Encoding) {
hash_data(req.http.Accept-Encoding);
}
if (req.http.Cookie) {
hash_data(req.http.Cookie);
}
}
Cấu hình đã loại bỏ các unkeyed query string trong request nhận vào:
if (req.url ~ "^\?.*") {
return(pass);
}
3. Unkeyed query parameters
Trong nhiều trường hợp, các nhà phát triển đã chú ý đến vấn đề bảo mật bằng cách loại bỏ các tham số vô nghĩa như trên. Tuy nhiên, do liên quan đến các yêu tố khác, một số tham số đặc biệt vẫn được phép sử dụng. Chẳng hạn, để thu thập dữ liệu từ các nguồn lưu lượng truy cập, ứng dụng đã sử dụng các tham số Urchin Tracking Modules (UTM).
UTM là một bộ gồm năm tham số cho phép ứng dụng xác định thông tin và tính chất các nguồn truy cập tới trang web hiện tại.
utm_source
Tên trang web chứa liên kết mà người dùng đã nhấp.utm_medium
: Loại lưu lượng.utm_campaign
: Chiến dịch marketing hoặc quảng cáo chịu trách nhiệm.utm_term
: Từ khóa, truyền thông tin, ...utm_content
: Các thông tin thêm.
Ví dụ cấu hình Varnish Cache cho phép các tham số UTM:
sub vcl_recv {
// ...
# Enable caching
if (req.http.Cache-Control !~ "no-cache") {
unset req.http.Cookie;
# Allow utm parameters
if (req.url ~ "(utm_source|utm_medium|utm_campaign|utm_term|utm_content)") {
set req.url = regsuball(req.url, "(?<=\?|&)(utm_(source|medium|campaign|term|content))=[^&]*", "\1");
}
set req.http.host = regsub(req.http.host, ":[0-9]+", "");
return(hash);
}
// ...
}
Bởi vậy, đừng quên kiểm tra các tham số đặc biệt như UTM parameters trong quá trình tìm kiếm lỗ hổng web cache poisoning. Tùy chọn Guess GET parameters trong extension Param Miner hỗ trợ việc tìm kiếm:
Các bạn có thể luyện tập thêm trong bài lab Web cache poisoning via an unkeyed query parameter.
Để ngăn chặn tấn công web cache poisoning thông qua các tham số utm, ta có thể sử dụng cấu hình Varnish Cache như sau:
sub vcl_recv {
if (req.url ~ "^\?.*") {
if (req.url ~ "utm_") {
return (pass);
} else {
set req.url = regsub(req.url, "\?.*", "");
}
}
set req.http.X-Real-IP = client.ip;
set req.http.X-Forwarded-For = client.ip;
set req.http.host = regsub(req.http.host, ":[0-9]+", "");
return (hash);
}
sub vcl_hash {
hash_data(req.url);
if (req.http.host) {
hash_data(req.http.host);
}
if (req.http.Accept-Encoding) {
hash_data(req.http.Accept-Encoding);
}
if (req.http.Cookie) {
hash_data(req.http.Cookie);
}
}
Trong đó, một điều kiện mới được thêm vào sub vcl_recv, nếu tham số trên URL bắt đầu bằng utm_
thì request sẽ không được cache và được trả về ngay lập tức. Các tham số utm thường được sử dụng cho mục đích theo dõi người dùng, do đó, việc loại bỏ chúng có thể gây mất tính năng theo dõi đối với ứng dụng web. Tuy nhiên, đối với nhiều trường hợp, không sử dụng tính năng này có thể tăng tính riêng tư cho người dùng và đảm bảo vấn đề bảo mật.
Các tài liệu tham khảo
- https://portswigger.net/web-security/web-cache-poisoning/exploiting-design-flaws
- https://portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws
- https://portswigger.net/research/practical-web-cache-poisoning
- https://portswigger.net/research/web-cache-entanglement
- https://varnish-cache.org/docs/3.0/reference/index.html
©️ Tác giả: Lê Ngọc Hoa từ Viblo
All rights reserved