---
# System prepended metadata

title: Web cache poisoning

---

# Web cache poisoning
WCP là kỹ thuật tấn công mà atker đầu độc cache của máy chủ hoặc proxy bằng response độc hại.
Ảnh hưởng của cuộc tấn công này dựa trên mức độ nguy hại của payload được chèn vào.

Quá trình dựng một tấn công WCP:
1. Identify and evaluate unkeyed inputs
2. Elicit a harmful response from the back-end server
3. Get the response cached

## Exploiting cache design flaws
Khai thác thiếu sót trong thiết kế cache.



## Exploiting cache implementation 
Khai thác sự kì quặc trong triển khai caching system.

### Cache key flaws
URL path và query string không được xem là phù hợp cho WCP. Ví nó là dữ liệu động.
Tuy nhiên, hành vi của cache system hoặc CDN có thể có sai sót. Ví dụ như:
-  Bỏ bớt query string
-  Lọc bỏ một số query param cụ thể.
-  Chuẩn hoá input trước khi tạo key.
Khiến victim không cần nhập URL path hay query string tương ứng nhưng vẫn bị poison.

### Cache probing methodology
probe: thăm dò
Phương pháp thăm dò sai sót triển khai cache khác với phương pháp WCP cổ điển.
Các website khác nhau triển khai và cấu hình cache khác nhau -> Hiểu sâu hành vi của cache.
Các phương pháp bao gồm:
- Identify a suitable cache oracle
- Probe key handling
- Idenify an exploitable gadget

#### Identify a suitable cache oracle
Bước đầu cần xác định cache oracle phù hợp. Cache oracle là page hoặc endpoint xác nhận hành vi của cache. Có thể thể hiện qua:
- HTTP header chỉ rõ cache hit.
- Sự thay đổi có thể quan sát của nội dung động.
- Sự khác biệt trong response times.

Lí tưởng, cache oracle có thể reflect toàn bộ URL và một query param trong response. Giúp xác định sự khác biệt trong parsing giữa cache và application.
Khi xác định được cache bên thứ 3 được sử dụng, có thể khai thác qua tài liệu về nó. Ví dụ, Akamai hỗ trợ header `Pragma: akamai-x-get-cache-key`:
```
GET /?param=1 HTTP/1.1
Host: innocent-website.com
Pragma: akamai-x-get-cache-key

HTTP/1.1 200 OK
X-Cache-Key: innocent-website.com/?param=1
```

#### Probe key handling
Cache có thể thực hiện xử lí với input khi tạo cache key. Thường là loại bỏ query param cụ thể, toàn bộ query string hoặc port khỏi "Host" header.
```
GET / HTTP/1.1
Host: vulnerable-website.com:1337

HTTP/1.1 302 Moved Permanently
Location: https://vulnerable-website.com:1337/en
Cache-Status: miss


GET / HTTP/1.1
Host: vulnerable-website.com

HTTP/1.1 302 Moved Permanently
Location: https://vulnerable-website.com:1337/en
Cache-Status: hit
```

#### Unkeyed query string
Loại bỏ toàn bộ query string trong request line.

##### Detecting an unkeyed query string


#### Identiry an exploiable gadget
Sau khi hiểu rõ hành vi cache và thiết sót trong dựng cache key. Thực hiện tìm gadget phù hợp.
Gadget thường là lỗ hổng client-side (XSS, open redirects, ...).

### Exploing cache key flaws
Xem xét một số sai sót cache key tiêu biểu:

#### Unkeyed port
"Host" header thường là một phần của cache key. Nếu caching system parse và loại bỏ port khỏi cache thì có thể bị poison.
Có thể chèn payload XSS thay vì port number nếu website không xử lí kĩ.
```
GET / HTTP/1.1
Host: vulnerable-website.com:1337

HTTP/1.1 302 Moved Permanently
Location: https://vulnerable-website.com:1337/en
Cache-Status: miss


GET / HTTP/1.1
Host: vulnerable-website.com

HTTP/1.1 302 Moved Permanently
Location: https://vulnerable-website.com:1337/en
Cache-Status: hit
```

#### Unkeyed query string
Loại bỏ toàn bộ query string trong request line. Khiến query string không được dùng để xây dựng cache key

##### Detecting an unkeyed query string
Cách phát hiện Query String bị bỏ qua trong cache key:
1. Có header cache feedback (ví dụ Cache-Status: hit/miss):
Gửi request thay đổi `?param=abc` thành `?param=xyz` nếu luôn nhận được cached response.
2. Không có header phân biệt cache hit/miss:
Sử dụng kỹ thuật cache buster: Thêm các header có khả năng được dùng trong cache key, nhưng không ảnh hưởng logic ứng dụng:
```
Accept-Encoding: gzip, deflate, cachebuster
Accept: */*, text/cachebuster
Cookie: cachebuster=1
Origin: https://cachebuster.vulnerable-website.com
```
3. Path Normalization
Khai thác sự khác biệt giữa cách cache và backend xử lý path.
Dù đường dẫn có khác nhau:
Apache: `GET //`
Nginx: `GET /%2F`
PHP: ```GET /index.php/xyz```
.NET: `GET /(A(xyz)/`
Nhưng server backend có thể coi những path này là tương đương với `GET /`.

##### Exploiting an unkeyed query string
Loại bỏ query string khỏi cache key khiến lỗ hổng rXSS nghiêm trọng hơn, vì victim nhận mã độc mà không cần tương tác trực tiếp với URL độc.

#### Unkeyed query parameters
so far: cho đến nay
Nhiều website chỉ bỏ qua một số param cụ thể khi tạo cache key. Những tham số này thường:
- Không ảnh hưởng logic xử lý backend.
- Thường là tracking / quảng cáo / analytics param.
Ví dụ như param "utm_content".
Tuy những param này không ảnh hưởng trực tiếp nhưng vẫn có nguy cơ nếu ứng dụng sử dụng mà không xử lí.

#### Cache parameter cloaking
Lỗi trong sự đồng bộ xử lí param của Cache và Server.

`GET /?example=123?excluded_param=mal`

||||
|-----|--------|-----|
|Cache parser|Phân tích 2 dấu `?` thành 2 param: `example=123` và `excluded_param=mal` |Bỏ qua `excluded_param`, tạo cache key cho `example=123`|
|Server parser|Chỉ phân tích dấu `?` đầu tiên, nhận toàn bộ `example=123?excluded_param=mal` là giá trị của `example` param|Payload `mal` vẫn đến được server|

##### Exploiting parameter parsing quirks
Server có param parser riêng biệt so với Cache.
- Trong "Ruby on Rails" có sử dụng `;` như một kí tự phân tách tham số (tương tự `&`). Đồng thời, nếu có 2 param trùng lặp, nó sẽ sử dụng param phía sau.

`GET /?keyed_param=abc&excluded_param=123;keyed_param=mal`

||||
|-----|--------|-----|
|Cache parser|Phân tích 2 dấu `&` thành 2 param: `keyed_param=abc` và `excluded_param=123;keyed_param=mal`|Bỏ qua `excluded_param`, tạo cache key cho `keyed_param=abc`|
|Server parser (Ruby on Rails)|Phân tích dấu `&` và `;` đều là kí tự phân cách, nhận 3 param: `keyed_param=abc`, `excluded_param=123` và `keyed_param=mal`|Payload `mal` vẫn đến được server|

##### Exploiting fat GET support
Server vô tình xử lí những dữ liệu vốn là không đúng logic. Ví dụ như việc dù là GET request nhưng Server vẫn nhận dữ liệu từ body. Nên khi trang được cache, phần payload có thể được đính kèm.
Đôi khi có thể ghi đè HTTP method bằng `X-HTTP-Method-Override` header.
|||
|-----|--------|
|Cache|Chỉ đọc dòng đầu `GET /?param=innocent`, tạo cache key theo đó.|
|Server|Đọc body `param=mal`, xử lý như POST (có thể cần X-HTTP-Method-Override).|

##### Exploiting dynamic content in resource imports
Sai sót khi mà các file thường là tĩnh (như CSS, ...) lại bị tác động bởi các tham số động trong URL.
Một số file tài nguyên như style.css có thể phản chiếu giá trị query string vào nội dung response:
```
GET /style.css?excluded_param=123);@import… HTTP/1.1
```
```
HTTP/1.1 200 OK
…
@import url(/site/home/index.part1.8a6715a2.css?excluded_param=123);@import…
```
Trang HTML không có `<!DOCTYPE html>` nên browser dễ thực thi các HTML response trong file CSS.
Server lỗi trả về `Content-Type: text/html` thay vì `text/css`.
`GET /style.css?excluded_param=alert(1)%0A{}*{color:red;}`
- Server phản hồi lỗi, nhưng nội dung HTML chứa đoạn:
`This request was blocked due to…alert(1)
{}*{color:red;}`
- Trình duyệt bắt nhầm CSS và cố gắng thực thi phần `*{color:red;}` trong context CSS.

#### Normalized cache keys
Kỹ thuật này lợi dụng việc cache server thực hiện normalization các param khi tạo cache key, dẫn đến nhiều URL khác nhau có thể tạo ra cùng một cache key.
1. Ví dụ
Có lỗ hổng XSS reflected:
`GET /example?param="><script>alert(1)</script>`
Nhưng lỗ hổng khó khai thác vì: Trình duyệt tự động URL-encode các ký tự đặc biệt. Server không decode, nên phản hồi chỉ chứa chuỗi encode (`%22%3e...`), không thực thi được.
Một số hệ thống cache (Varnish, CDN như Cloudflare, Reverse proxy tùy chỉnh) sẽ normalize tham số trước khi tạo cache key, ví dụ:
`GET /example?param="><script>alert(1)</script>`
`GET /example?param=%22%3e%3cscript%3ealert(1)%3c/script%3e`
- Hai URL này có thể bị chuẩn hóa thành cùng một cache key!
2. Kịch bản tấn công
Attacker gửi request "raw" chưa encode (qua Burp Repeater hoặc curl):
`GET /example?param="><script>alert(1)</script>`
- Server phản hồi với payload chưa encode, bị lưu vào cache (vì cache key normalize giống nhau).
- Victim truy cập URL encode chuẩn (trình duyệt gửi):
`GET /example?param=%22%3e%3cscript%3ealert(1)%3c/script%3e`
- Do cache key giống nhau, victim nhận về response độc đã bị cache.
- XSS xảy ra client-side dù request của victim là encode.

#### Cache key injection
Cách hệ thống tạo cache key
Hệ thống lấy nhiều đoạn thông tin (ví dụ: URL, query string, header quan trọng như Origin hay User-Agent)
Nối chúng lại thành một chuỗi theo định dạng cố định, ví dụ:
`<URL>__Origin=<giá trị Origin>__...`
Cache key là chuỗi này; mọi response với cùng key sẽ được lưu và tái sử dụng.

##### Lỗ hổng từ delimiter không được escape
Nếu “delimiter” (ở đây là `__`) không được xử lý an toàn, kẻ tấn công có thể đóng vai trò làm chính khách hàng gửi đến server một giá trị header chứa luôn `__`.
`Origin: '-alert(1)-'__`
Khi nối thành cache key, chuỗi sẽ thành:
`/path?param=123__Origin='-alert(1)-'__`
Thậm chí nếu server không phân tách đúng, payload sẽ nằm ngay trong cache key.

##### Khai thác qua cache poisoning và collision
Attacker gửi một request hợp lệ có header Origin chứa payload:
```
GET /path?param=123 HTTP/1.1
Origin: '-alert(1)-'__
```
Server xử lý, trả về response có nội dung chứa payload (ví dụ script) và header:
`X-Cache-Key: /path?param=123__Origin='-alert(1)-'__`
Response này được lưu vào cache dưới key trên.
Attacker lừa nạn nhân truy cập một URL đã bao gồm payload ngay trong đường dẫn, sao cho cache key của nó giống hệt key đã poison:
`GET /path?param=123__Origin='-alert(1)-'__ HTTP/1.1`
Hệ thống cache so sánh cache key (từ URL và header mặc định của nạn nhân) và nhận ra khớp với key đã có, nên trả về ngay response cũ (có chứa `<script>…'-alert(1)-'…</script>`).
Nạn nhân bị chạy mã độc mà không hề biết.
Minh họa cụ thể:
```
# Bước 1: Poison cache
GET /path?param=123 HTTP/1.1
Host: vulnerable.example
Origin: '-alert(1)-'__               ← Chèn payload + delimiter

▶ Server trả về (status 200) và lưu cache:
HTTP/1.1 200 OK
X-Cache-Key: /path?param=123__Origin='-alert(1)-'__
Content-Type: text/html

<html>
  <body>
    <script>
      // Payload đã chèn
      alert('XSS via cache poisoning');
    </script>
  </body>
</html>
```
```
# Bước 2: Thực thi collision trên máy nạn nhân
GET /path?param=123__Origin='-alert(1)-'__ HTTP/1.1
Host: vulnerable.example
# (Origin header bình thường hoặc vắng mặt)

▶ Cache hit, server trả lại response đã poisoned
HTTP/1.1 200 OK
X-Cache-Key: /path?param=123__Origin='-alert(1)-'__
X-Cache: hit
Content-Type:
```