PostMessage Vulnerabilities - J2EEScan – Javascript postMessage Handler Detected

23rd Feb 2022
Table of contents

Nếu bạn không muốn nhận tin nhắn từ các trang khác, đừng thêm bất kỳ trình nghe sự kiện nào cho các sự kiện tin nhắn. Đây là một cách chứng minh hoàn toàn đánh lừa để tránh các vấn đề về bảo mật. Nếu bạn muốn nhận tin nhắn từ các trang khác, hãy luôn xác minh danh tính của người gửi bằng cách sử dụng nguồn gốc và có thể là thuộc tính nguồn. Bất kỳ cửa sổ nào (bao gồm, ví dụ: http://evil.example.com) đều có thể gửi tin nhắn đến bất kỳ cửa sổ nào khác và bạn không có gì đảm bảo rằng người gửi không xác định sẽ không gửi tin nhắn độc hại. Tuy nhiên, đã xác minh danh tính, bạn vẫn phải luôn xác minh cú pháp của tin nhắn nhận được. Nếu không, một lỗ hổng bảo mật trên trang web mà bạn tin cậy để chỉ gửi các tin nhắn đáng tin cậy có thể mở ra một lỗ hổng tập lệnh giữa các trang web trong trang web của bạn. Luôn chỉ định nguồn gốc đích chính xác, không phải *, khi bạn sử dụng postMessage để gửi dữ liệu đến các cửa sổ khác. Một trang web độc hại có thể thay đổi vị trí của cửa sổ mà bạn không biết và do đó nó có thể chặn dữ liệu được gửi bằng postMessage.

This is the first part of a two series of articles about postMessage vulnerabilities. This part is an introduction to what is a postMessage, basic exploitation, detection and mitigation. The second part is an analysis of real cases reported in Bug Bounty scenarios.

What is a postMessage?

According to Mozilla:

The window.postMessage() method safely enables cross-origin communication between Window objects; e.g., between a page and a pop-up that it spawned, or between a page and an iframe embedded within it.

Let’s see an example.

Supose we have a main website (1.html) that communicates with another website (2.html). In the second website there is a back button that changes when the navigation in the first website changes. For example, in website1 we navigate to “changed.html”, then the back button in website2 points to “changed.html”. To do that, postMessage is used and sends the value of website1 to website2.

The code in 1.html is the following:

<!DOCTYPE html>
<html>
<head>
    <title>Website 1</title>
 <meta charset="utf-8" />
<script>

var child;
function openChild() {child = window.open('2.html', 'popup', 'height=300px, width=500px');
 
}
function sendMessage(){
    let msg={url : "changed.html"};
    // In production, DO NOT use '*', use toe target domain
    child.postMessage(msg,'*')// child is the targetWindow
    child.focus();
}</script>
</head>
<body>
    <form>
        <fieldset>
            <input type='button' id='btnopen' value='Open child' onclick='openChild();' />
            <input type='button' id='btnSendMsg' value='Send Message' onclick='sendMessage();' />
        </fieldset>
    </form>
</body>
</html>

There are two buttons:

    The first one opens a popup containing 2.html through openChild() function
    The second one sends a message through sendMessage() function. To do that a message is set defining msg variable and then calling postMessage(msg,'*').

The code in 2.html is the following:

<!DOCTYPE html>
<html>
<head>
    <title>Website 2</title>
    <meta charset="utf-8" />
    <script>
    // Allow window to listen for a postMessage
    window.addEventListener("message", (event)=>{
        // Normally you would check event.origin
        // To verify the targetOrigin matches
        // this window's domain
         document.getElementById("redirection").href=`${event.data.url}`;
        // event.data contains the message sent
      
    });function closeMe() {
        try {window.close();
        } catch (e) { console.log(e) }
        try {self.close();
        } catch (e) { console.log(e) }}
    </script>
</head>
<body>
    <form>
        <h1>Recipient of postMessage</h1>
            <fieldset>
                <a type='text' id='redirection' href=''>Go back</a>
                <input type='button' id='btnCloseMe' value='Close me' onclick='closeMe();' />
            </fieldset>
   
    </form>
</body>
</html>

There is a button and a link:

  • The link handles the back redirection. The href field changes according the data received with the listener window.addEventListener("message", (event). After receiving the message, the data in the event is read from event.data.url and passed to href.
  • The button closes the window calling the function closeMe()
J2EEScan – Javascript postMessage Handler Detected

A basic vulnerability

PostMessages if are not implemented correctly could lead to information disclosure or cross-site scripting vulnerabilities (XSS).

In this case, 2.html is expecting a message without validating the origin, therefore we could host a webpage 3.html that will load 2.html as an iframe and the invoke the postMessage() function to manipulate the href value.

<!DOCTYPE html>
<html>
<head>
    <title>XSS PoC</title>
 <meta charset="utf-8" />

</head>
<body>
    
 <iframe id="frame" src="2.html" ></iframe>

 <script>
     
    let msg={url : "javascript:prompt(1)"};
    var iFrame = document.getElementById("frame")
    iFrame.contentWindow.postMessage(msg, '*');

</script>
</body>
</html>

In this example, the malicious msg variable contains the data {url : "javascript:prompt(1)"};, that will be send to 2.html. 2.html after processing, will change the value in the <a href to the value of the msg.url. An iframe is used to load in the same website the attack scenario. When a user clicks the Go back link, a XSS will be executed.

The way to detect postMessage vulnerabilities

The way to detect postMessage vulnerabilities

This is a simple case, in PostMessage Vulnerabilities. Part II real case scenarios will be analyzed.

Mitigations

According to Mozilla:

  • If you do not expect to receive messages from other sites, do not add any event listeners for message events. This is a completely foolproof way to avoid security problems.
  • If you do expect to receive messages from other sites, always verify the sender’s identity using the origin and possibly source properties. Any window can send a message to any other window, and you have no guarantees that an unknown sender will not send malicious messages. Having verified identity, however, you still should always verify the syntax of the received message. Otherwise, a security hole in the site you trusted to send only trusted messages could then open a cross-site scripting hole in your site.
  • Always specify an exact target origin, not *, when you use postMessage to send data to other windows. A malicious site can change the location of the window without your knowledge, and therefore it can intercept the data sent using postMessage.

In the previous scenario, the following code should be modified in 1.html:

child.postMessage(msg,'*')

to

child.postMessage(msg,'2.html')

And in 2.html:

window.addEventListener("message", (event)=>{
    ...

}

To:

window.addEventListener("message", (event)=>{
    if (event.origin !== "http://safe.com")
    return;
    ...
}

Detection

The way to detect postMessage vulnerabilities is reading JavaScript code. There is not an easy automated tool to help with this because when a listener is defined, the event data flow must be followed to analyze if the code ends in a vulnerable function. Anyways, I recommend two ways to detect the function calls:

With J2EEScan, from git repository, not BApp Store because I think is not updated:

  • Github - J2EEScan

With BurpBounty, defining a set of Passive response strings searching for keywords like: postMessage , addEventListener("message, .on("message".

  • Github - Burp Bounty
Bạn thấy bài viết này như thế nào?
1 reaction

Add new comment

Image CAPTCHA
Enter the characters shown in the image.
Câu nói tâm đắc: “Điều tuyệt với nhất trong cuộc sống là làm được những việc mà người khác tin là không thể!”

Related Articles

String là một kiểu dữ liệu thông dụng trong nhiều ngôn ngữ lập trình, nó mạnh vì nó có thể dùng vào nhiều trường hợp để giải quyết các vấn đề

Nếu bạn muốn người dùng cung cấp tên, tuổi, email… ta cần một hàm Javascript  để đảm bảo rằng họ đã nhập với các thông tin này

The character jumping around the page was caused by having to finish the animation to the next unit before going on to the next key.

Gọi phương thức postMessage của phần tử window / iframe mà bạn muốn gửi thông tin tới.

Ví dụ sau minh họa một biểu mẫu HTML5 truy cập dữ liệu thông qua lệnh gọi API Ajax bằng cách sử dụng phương thức POST.