概念
由非緩存鍵導致的差異化響應都能夠被存儲並提供給其他用戶
儲備知識
Web緩存中毒的目的是發送導致有危害響應的請求,該響應將保存在緩存中並提供給其他用戶。
緩存通常通過CDN、負載均衡器或簡單的方向代理來實現。
緩存鍵(cache key):通過緩存鍵來判斷兩個請求是否正在嘗試加載相同的資源。
X-Forward-For:表示代理前的原始IP
x-Forwarded-For
(XFF)在客戶端訪問服務器的過程中如果需要經過HTTP代理或者負載均衡服務器,可以被用來獲取最初發起請求的客戶端的IP地址,這個消息首部成為事實上的標准。在消息流從客戶端流向服務器的過程中被攔截的情況下,服務器端的訪問日志只能記錄代理服務器或者負載均衡服務器的IP地址。如果想要獲得最初發起請求的客戶端的IP地址的話,那么X-Forwarded-For就派上了用場。
X-Forward-Host:表示原始的URL請求地址
**
The X-Forwarded-Host
**(XFH)是一個事實上的標准首部,用來確定客戶端發起的請求中使用Host指定的初始域名。
X-Forward-proto/scheme:表示當前請求以http/https的方式
X-Forwarded-Proto
(XFP) 是一個事實上的標准首部,用來確定客戶端與代理服務器或者負載均衡服務器之間的連接所采用的傳輸協議(HTTP 或 HTTPS)。在服務器的訪問日志中記錄的是負載均衡服務器與服務器之間的連接所使用的傳輸協議,而非客戶端與負載均衡服務器之間所使用的協議。為了確定客戶端與負載均衡服務器之間所使用的協議, X-Forwarded-Proto 就派上了用場。
Via:代理服務器在轉發時添加,作為標記。
Via
是一個通用首部,是由代理服務器添加的,適用於正向和反向代理,在請求和響應首部中均可出現。這個消息首部可以用來追蹤消息轉發情況,防止循環請求,以及識別在請求或響應傳遞鏈中消息發送者對於協議的支持能力。
Vary:賦予的值代表緩存鍵
Vary
是一個HTTP響應頭部信息,它決定了對於未來的一個請求頭,應該用一個緩存的回復(response)還是向源服務器請求一個新的回復。它被服務器用來表明在 content negotiation algorithm(內容協商算法)中選擇一個資源代表的時候應該使用哪些頭部信息(headers).
Vary *
表示請求被視為唯一並且非緩存的。這種情況更加建議使用Cache-Control:no-store
來實現。
Cache-Control:緩存機制,當值為no-store
時表示緩存中不得存儲任何關於客戶端請求和服務端響應的內容。每次由客戶端發起的請求都會下載完整的響應內容。當值為no-cache
時表示每次有請求發出時,緩存會將此請求發到服務器(譯者注:該請求應該會帶有與本地緩存相關的驗證字段),服務器端會驗證請求中所描述的緩存是否過期,若未過期(注:實際就是返回304),則緩存才使用本地緩存副本。當值為public
時表示該響應可以被任何中間人(譯者注:比如中間代理、CDN等)緩存。而 private
則表示該響應是專用於某單個用戶的,中間人不能緩存此響應,該響應只能應用於瀏覽器私有緩存中。max-age=<seconds>
表示資源能夠被緩存(保持新鮮)的最大時間。
X-Original-URL/X-Rewrite-URL:對這些報頭的支持允許用戶通過X-Original-URL
或X-Rewrite-URL
HTTP請求報頭重寫請求URL中的路徑,並允許用戶訪問一個URL,但web應用程序返回一個不同的URL,這可以繞過對更高級別緩存和web服務器的限制。
漏洞利用過程
1.判斷哪些非緩存鍵會影響頁面內容
任何的緩存投毒都依賴於非緩存鍵,所以我們在一開始就要判斷哪些HTTP頭部屬於緩存鍵,哪些不屬於。再通過修改或添加HTTP頭部來判斷哪些頭部會引起頁面內容的變化。常用的兩種方式:
- 手動修改或添加HTTP頭部,指定隨機字符來判斷頭部是否影響頁面內容
- 使用Brupsuite插件Param Miner來自動判斷,在burpsuite的URL右鍵選擇
Guess headers
注:在判斷非緩存鍵的時候,可能會在無意間導致響應緩存下來,這時有其他用戶訪問就會收到剛剛我們測試時的緩存。所以我們在手動測試的時候可以手動添加一個特定的緩存鍵,比如請求https://vulnerable.com/?abc
;如果使用的是burpsuite插件Param Miner則會自動添加隨機值。
2.構造內容引起服務器端的有害響應
針對不同的非緩存鍵,我們需要知道哪些非緩存鍵會導致頁面返回有害的內容。舉一個例子:頁面中js鏈接的域名是通過獲取HTTP頭部中的“X-Forwarded-Host”字段來設置的。而服務器不會將這個字段作為緩存鍵,那么這個字段就可以利用。
3.獲取響應,使有害內容被緩存
通過構造有害的內容,訪問頁面,獲取響應。就會將有害的內容存入緩存中。
緩存設計缺陷案例
1. X-forwarded-Host
如果網站以不安全的方式處理非緩存鍵的輸入並允許后續的HTTP響應被緩存,則他們很容易遭受Web緩存中毒。
比如
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" />
x-forwarded-host
頭的值用於動態生成image的URL,以上的案例可以這樣利用:
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" />
如果緩存了此響應,則將向/en?region=uk
訪問的所有用戶都會收到XSS影響。
2. cookie
Cookie有時也用於在響應中動態生成內容,如果cookie也存在非緩存鍵則也會收到影響。
3. X-Forwarded-scheme/X-forwarded-Proto
X-Forwarded-scheme/X-Forwarded-Proto頭:當值不為https時,表示當前請求以http的方式發送,一般情況下都會返回302跳轉到當前URL的https協議請求。當非緩存鍵是X-Forwarded-scheme頭時,如果網站同時支持X-Forwarded-Host則可以通過兩者結合達到web投毒的攻擊效果。
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
4. 返回緩存信息頭
暴露太多的響應信息也可能會讓攻擊更容易。
HTTP/1.1 200 OK
Via: 1.1 varnish-v4
Age: 174
Cache-Control: public, max-age=1800
這里暴露出了緩存的機制和時間,攻擊者可以根據此時間來操作。不用大量的重放攻擊。
e. Vary頭一般情況下值為User-Agent
,表示UA也作為緩存鍵,根據這個,我們可以通過Web緩存攻擊特定的UA用戶。
f. 有時Web會使用Json
傳參,並通過JavaScript操作數據,這種情況下就有可能導致基於DOM的XSS問題。
我們需要操作的是讓我們攻擊服務器上的惡意Json
文件投毒到緩存,注意⚠️:如果使用Web緩存中毒使網站從服務器加載惡意Json數據,則需要使用CORS授予網站訪問JSON的權限,像下面一樣。
HTTP/1.1 200 OK
Content-Type: application/json
Access-Control-Allow-Origin: *
{
"malicious json" : "malicious json"
}
緩存實現缺陷案例
探測緩存實現缺陷的方法與經典的web緩存中毒方法略有不同。這些較新的技術依賴於緩存的特定實現和配置中的缺陷,這些缺陷可能因站點而異。
1.Host頭忽略端口
緩存機制在判斷緩存鍵時存在缺陷,比如緩存在解析Host
頭時只取了主機部分,直接忽略掉了端口部分,所以可以設置特殊的端口來投毒。
2.查詢字符串為非緩存鍵
GET /?a=1'><script>alert(1)</script> HTTP/1.1
Host: acc81f031e21840280fb449d0046000f.web-security-academy.net
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Connection: close
Cache-Control: max-age=35
Age: 13
X-Cache-Key: /$$
X-Cache: hit
通過X-Cache-Key: /$$可以知道?a=1'><script>alert(1)</script>
未被識別成緩存鍵,這樣就成功投毒了
3.緩存參數偽裝
GET /?example=123?excluded_param=bad-stuff-here
上面能成功投毒的前提有:
- 緩存將上面識別成兩個參數,並且緩存鍵會排出掉第二個參數
- Web服務器不接受第二個
?
為參數分隔符,將其全部認為是一個參數
這樣就可以將第二個參數的惡意代碼投毒成功了。
4.利用參數解析怪癖(Ruby on Rails框架)
Ruby on Rails框架將與符號(&)和分號(;)都解釋為定界符
GET /?keyed_param=abc&excluded_param=123;keyed_param=bad-stuff-here
緩存將請求解析為兩個參數,並且會從緩存鍵去除第二個:
-
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
keyed_param參數出現了重復,在框架Ruby on Rails中這種情況會取最后出現的值,這樣就成功的將惡意代碼投毒了。
比如:
GET /jsonp?callback=innocentFunction&excluded_param=123;callback=alert(1)
5.支持胖GET方法
GET /?param=innocent HTTP/1.1
…
param=bad-stuff-here
在這種情況下(比較少見),緩存鍵將基於請求行,但是參數的服務器端值將從正文中獲取。
如果網站不支持帶body的GET,可以嘗試使用請求頭:X-HTTP-Method-Override
來告訴服務器使用POST覆蓋GET
6.規范化的緩存鍵
一些緩存機制會在添加緩存鍵時實現規范化,意思就是以下兩種請求將視為同一個緩存鍵
GET /example?param="><test>
GET /example?param=%22%3e%3ctest%3e
正常我們在瀏覽器提交URL時,瀏覽器會自動encode。
使用Burp Repeater發送未經encode的惡意請求來毒害緩存,當受害者通過瀏覽器發出被encode的請求時就會響應被毒害的緩存。
7.緩存鍵注入
請求行和Origin頭都是緩存鍵,發現Origin的值會影響響應的值,這種情況下我們可以使用緩存鍵注入的方式來達到投毒的效果。
GET /path?param=123 HTTP/1.1
Origin: '-alert(1)-'__
HTTP/1.1 200 OK
X-Cache-Key: /path?param=123$$Origin='-alert(1)-'$$
<script>…'-alert(1)-'…</script>
然后誘使受害用戶訪問以下URL,則會向他們提供中毒的響應:
GET /path?param=123$$Origin='-alert(1)-'$$ HTTP/1.1
HTTP/1.1 200 OK
X-Cache-Key: /path?param=123$$Origin='-alert(1)-'$$
X-Cache: hit
<script>…'-alert(1)-'…</script>