คลัง
web

Web Cache Poisoning

Web Cache Poisoning คือการทำให้ cache (CDN/reverse proxy) เก็บ response ที่มี payload ของผู้โจมตี แล้วเสิร์ฟให้ผู้ใช้คนอื่นทุกคนที่ขอ path เดียวกัน อาศัย 'unkeyed input' — ส่วนของ request ที่มีผลต่อ response แต่ไม่ถูกนำมาคำนวณเป็น cache key บทนี้ไล่ตั้งแต่หลักการ cache key, การหา unkeyed input ด้วย cache buster + Param Miner, การยกระดับเป็น XSS/redirect/DoS, fat GET และ cache-key normalization bugs

Advanced#cache-poisoning#cache#unkeyed-input#cache-buster#fat-get#cache-deception#web#ctf

1. หลักการ — cache key และ unkeyed input

Cache ตัดสินใจว่า request สองอันเป็น 'อันเดียวกัน' จาก cache key ซึ่งโดยทั่วไป = method + host + path + query string + บาง header (เช่น Accept, Cookie ที่ config ไว้) ถ้ามีส่วนของ request ที่มีผลต่อ response แต่ไม่อยู่ใน cache key — เรียกว่า unkeyed input — ผู้โจมตีใส่ payload ลงใน unkeyed input นั้น response ที่ถูก 'poison' จะถูก cache ด้วย key เดิม แล้วเสิร์ฟให้เหยื่อทุกคนที่ขอ path เดียวกัน โดยเหยื่อไม่ต้องส่ง header ผิดอะไรเลย

Cache poisoning — unkeyed input ติดไปกับ cache
attacker X-Forwarded-Host: evil Cache (CDN) key = path เท่านั้น origin server เหยื่อทุกคน response ที่ poison (evil) ถูก cache → เสิร์ฟซ้ำ
เงื่อนไข 2 ข้อที่ต้องมีพร้อมกัน: (1) มี unkeyed input ที่มีผลต่อ response และ (2) response นั้น ถูก cache จริง (มี cacheable path/extension หรือ header ที่บอกให้ cache)

2. ตรวจว่า response ถูก cache ไหม

ก่อนอื่นต้องยืนยันว่า path เป้าหมาย cacheable และหา indicator ของ cache hit/miss — ดูจาก response header

Header / สัญญาณความหมาย
X-Cache: hit / missเสิร์ฟจาก cache (hit) หรือ origin (miss) — พบใน Varnish, CloudFront
CF-Cache-Status: HIT/MISS/DYNAMICสถานะ cache ของ Cloudflare
Age: 120response อยู่ใน cache มาแล้วกี่วินาที (มี = ถูก cache)
Cache-Control: public, max-age=อนุญาตให้ cache และนานแค่ไหน
X-Cache-Hits: 5จำนวนครั้งที่ถูก serve จาก cache
Vary: User-Agentheader ที่ถูกนับเป็นส่วนหนึ่งของ key (แตก cache ตามค่านี้)
ใช้ cache buster เสมอตอนทดสอบ: เติม query param สุ่ม เช่น ?cb=12345 เพื่อให้แต่ละรอบทดสอบมี cache key ของตัวเอง จะได้ไม่ poison ผู้ใช้จริงและไม่โดน response เก่าที่ค้างอยู่รบกวนผล

3. หา unkeyed input (Param Miner)

  1. 1เพิ่ม cache buster (?cb=random) เพื่อแยก cache key ของ session ทดสอบออกจากผู้ใช้จริง
  2. 2ส่ง header ต้องสงสัย: X-Forwarded-Host, X-Forwarded-Scheme, X-Host, X-Forwarded-For, X-Original-URL
  3. 3ดูว่า header นั้นทำให้ response เปลี่ยนไหม (เช่น URL ใน